Commit Graph
63 Commits
Author SHA1 Message Date
glm-5.3-flash 0bc5a541ac Resolve N2: bound align annotations at 4096 (review #006)
align: 2^62 compiled and reported total_size = 2^63 — meaningless
layout output the consumer may act on, and the reachable path to the
MAX_ARRAY_BYTES cap used exactly this knob.

- MAX_ALIGN = 4096 (page granularity) in schema.rs, documented with the
  probe arithmetic
- parse_align returns Result and rejects over-cap values with a clean
  Schema error naming path/value/maximum — a silent clamp was rejected
  (it would change layout semantics without telling the consumer);
  both call sites thread the path, so standalone BastDoc::new (which
  never runs the meta-schema) is covered
- Meta-schema: "maximum": 4096 on StructDef.align and FieldDef.align —
  the published alk.dev/bast/v1/schema contract now matches the parser
- H1's byte-cap test retuned to align 4096 x count 2^16 = 2^28 > 2^26
  (the byte cap stays reachable under the new align cap)

Tests: 3 new (struct align above cap, field align above cap, align at
cap accepted). 511 tests green, clippy -D warnings clean, wasm32 build
green, cargo doc zero warnings.
2026-09-02 19:46:51 +00:00
glm-5.3-flash 8739d29550 Resolve L2/L3: real schema threaded through ReadPlan compile; dead locals dropped (review #006)
L2: compile() builds Arc<Value> once and threads &Arc<Value> down the
compile walk; every real ReadPlan carries the document at construction
— the Value::Null-placeholder-then-map-overwrite dance is gone. The one
remaining Null in wrap_leaf is documented as correct-by-construction
(anonymous synthetic wrapper, never escapes).

L3: materialize_plan_field drops its plan parameter (taken solely to
discard) and the field-disc union arm's disc_field/let _ pair is
deleted — the order-walk + by-name capture is the materializer's
correct design, as the finding's parity note described.

508 tests green, clippy -D warnings clean, wasm32 build green,
cargo doc zero warnings.
2026-09-02 19:43:51 +00:00
glm-5.3-flash dcfe9d16ff Fix M1/M2/M3: ADR-006 record gap + aligned record coverage + dead Struct arm (review #006)
M1: field_variable_kind (offset_map) now matches Record — a non-final
inline length-prefixed record field in aligned mode hits the ADR-006
rejection instead of computing silently corrupt offsets (probe-verified
clobber in the review: counts prefix at 0, id at 4).

M2: aligned record path locked with public-path tests —
materialize_aligned roundtrip (record<uint16>, wire arithmetic
asserted) and engine validate_bytes roundtrip + corrupted-buffer
rejection (record<uint32>). Record-as-last-field is the only safe
inline position post-M1.

M3: read_field's unreachable Struct arm (no struct-path entry ever
exists in an OffsetMap) replaced with a documented defensive Offset
error; doc comment states struct paths have no entry and the Offset
miss is the reachable composite failure. FieldValue::Struct (public
API, constructed by the packed reader) untouched.

Tests: 7 new (2 offset_map, 2 materialize, 1 engine M3 lock, plus the
roundtrip pair). 508 tests green, clippy -D warnings clean, wasm32
build green, cargo doc zero warnings.
2026-09-02 19:40:09 +00:00
glm-5.3-flash 5e74b991ac Fix H2: shared reference-graph guard for standalone walkers (review #006)
Cyclic or over-deep $ref graphs stack-overflowed the three standalone
schema walkers (OffsetMap::compute, LayoutBuilder::new,
materialize_aligned) — SIGABRT on probe, parity-preserved from 0.2.0.

- New src/walk_guard.rs: check_ref_graph() — one bounded walk over the
  reachable reference graph (depth cap 128 matching the plan compilers,
  path-scoped cycle set; diamonds allowed, cycles and 201-def chains
  rejected with the plan compilers' error wording)
- All three walkers run the guard at entry, before any recursion;
  materialize_aligned's is defense-in-depth (a cyclic doc can no longer
  produce an OffsetMap, but mismatched doc/map inputs must still fail
  cleanly)
- Behavioral side effect, net-positive: the guard eagerly parses every
  reachable def, so an invalid non-root def now surfaces at
  LayoutBuilder::new instead of build() — four H3 tests updated to
  expect the same Schema error earlier
- Test family: 12 new tests (walk_guard, offset_map, layout_builder,
  materialize) covering self/two-def/composite-carrier cycles, deep
  chains, and diamond non-rejection; no stack-overflow reproducers
  in-tree per the review's Methodology warning
- Stale "walkers have no cycle guard" statements updated in
  validation.md, 030 plan, ADR-012, and the engine gate comment

Verified: 501 tests green (423 + 17 + 34 + 15 + 12 + 2 ignored),
clippy -D warnings clean, wasm32 build green, cargo doc zero warnings.
2026-09-02 19:34:24 +00:00
glm-5.3-flash 05a2a42983 Fix H3: field-disc union wire convention — shared-then-variant (review #006)
Decision (recorded as an ADR-011 addendum): the packed-mode wire layout
for a field-name-discriminator TUnion is shared-then-variant — the
union's declared `fields` (disc + shared fields) first, then the
variant's own fields. Reader and materializer already implemented this;
LayoutBuilder was corrected from variant-only layout.

Enforcement in BastUnion::parse (the choke point every consumer
inherits — union roots at BastDoc::new, referenced unions at
resolve_ref):
- discriminator field must be declared in `fields`
- `fields` must not contain duplicate names
- variants must not re-declare shared fields (checked inline and
  through $ref resolution — parse chain now threads the doc root)
- the discriminator field must be the FIRST entry in `fields` (the
  reader reads the disc at the union start; a later position made it
  dispatch on the wrong bytes — H3 item 2, probe-verified)

Schemas relying on the old variant-only builder convention (variants
re-declaring shared fields) are rejected with a clean Schema error
naming the convention. Breaking for 0.2.0-era re-declaring schemas;
announced with 0.3.x.

- L5: FieldValue::Union::variant_start doc now states per-kind
  semantics (byte-disc: union_start + disc.offset + disc.size;
  field-disc: after the shared walk).
- L6: roundtrip test added (poc_roundtrip.rs) — LayoutBuilder write →
  SequentialReader read → materialize_packed → validate_bytes over a
  field-disc union with a second shared field and non-redeclaring
  variant; pins event.type@0/seq@1/handle@5, total 10.
- ADR-011: Status-block addendum recording the convention decision,
  the no-re-declare rule, and the breaking-constraint note.
- Review #006 updated: H3/L5/L6 resolution blocks, resolution log,
  recommended order.

Verified: 488 tests green (410+17+34+15+12, 2 pre-existing ignored),
clippy -D warnings clean, wasm32 build green, cargo doc zero warnings.
2026-09-02 18:37:51 +00:00
glm-5.3-flash 2d166f567b Fix H1: bound untrusted array counts; resolve L1 (review #006)
- Replace the three Vec::with_capacity(count) sites in materialize.rs
  with Vec::new() — validate_bytes on an adversarial count no longer
  OOM-aborts the process (AGENTS.md §3).
- New compile-time caps in schema.rs: MAX_ARRAY_ELEMENTS (2^16,
  enforced at BastArray::parse — the choke point every consumer
  inherits, bounds the walkers' per-element entry loops) and
  MAX_ARRAY_BYTES (2^26, enforced per walker against the mode-specific
  stride: compile_array, walk_array, compute_array_field).
- L1: fixed_composite_size/fixed_plan_size now return
  Result<Option<usize>>; unwrap_or_default() gone, overflow is a clean
  Schema error instead of silent stride-0.
- Zero-progress guard: stride-0 arrays whose elements consume 0 bytes
  (legal empty-struct elements) now error in plan_walk_variable_array_
  size and materialize_array_packed instead of looping count times.
- Tests: 8 new (parse/build/compile rejections, cap boundary,
  short-buffer clean error) + array_count_large_u64_parses_on_64bit
  rewritten to assert the new cap rejection. In-tree tests assert only
  the safe (compile-time) half per review #006's Methodology warning.
- Review #006 updated: H1/L1 resolution blocks, new finding N2
  (unbounded align annotations, found while re-deriving the cap
  arithmetic), resolution log, recommended order.

Verified: 482 tests green (405+17+34+14+12, 2 pre-existing ignored),
clippy -D warnings clean, wasm32-unknown-unknown build green.
2026-09-02 16:46:46 +00:00
glm-5.3-flash 27be01af93 Add review #006: 0.3.0 post-implementation review
Audits the shipped 0.3.0 code (commits ff85258..9949f91) for
correctness, untrusted-input discipline, 0.2.0 parity, code smells,
and coverage. Gates re-run green in-session (474 tests, clippy -D
warnings, doc, wasm32, publish --dry-run); coverage measured with
cargo-llvm-cov (89.59% lines / 84.80% fn).

Findings:
- H1: huge declared array count OOM-aborts validate_bytes (three
  Vec::with_capacity(count) sites; release-blocking)
- H2: cyclic $ref stack-overflows OffsetMap::compute /
  LayoutBuilder::new / materialize_aligned when driven standalone
  (engine gated, public walkers not; parity-preserved)
- H3: field-disc unions — builder (variant-only layout), reader
  (disc at union start), and materializer (position-correct shared
  walk) disagree on layout and discriminator position; needs a
  convention decision
- M1: ADR-006 check misses non-final inline Record fields (probe-
  confirmed)
- M2: aligned-mode record fields untested end-to-end
- M3: read_field's aligned Struct arm is unreachable dead code
- M4: coverage weak spots (materialize.rs 64.5% lines; aligned
  nested-struct recursion 0 executions through any test)
- L1-L6, N1: stride unwrap_or_default conflation, Null-schema
  placeholder, dead locals, linear-scan get, variant_start doc gap,
  missing field-disc roundtrip test, tunion/reader disc-kind split

Includes an operational warning: the H1/H2 reproducers OOM/stack-
abort the test harness — reproduce in an isolated process only.

Verification: file-only change, no code touched; suite green before
commit.
2026-09-02 15:06:26 +00:00
glm-5.3-flash 9949f914df Release v0.3.0: compiled forms — ReadPlan, owned BastDoc, LeafMeta, ValidationPlan, fingerprinting
Public API bump 0.2.0 -> 0.3.0 (the 030-compiled-forms plan is now
fully implemented; all eight phases landed).

- Cargo.toml: version 0.3.0. lib.rs re-exports complete (ReadPlan +
  sub-types, LeafMeta, OffsetEntry, ValidationPlan + sub-types).
- ADR-007 "Cost" rewritten to the Arc<ReadPlan> cost (15.7 ns) with
  the 0.2.0 "re-parse on demand" framing as a historical note
  (review #004 L2, the last loose end from that review).
- ADR-011/012 status blocks flipped to implemented; architecture
  README ADR table rows updated; layout-engine.md rewritten for the
  0.3.0 surface (engine-factory reader construction, OffsetMap
  OffsetEntry/LeafMeta/fingerprint section, owned BastDoc compute
  signature); SequentialReader module doc points at the engine
  factory. Reviews #004 and #005 flipped to closed.
- Bench re-run (alktty wire_vs_bast, 0.3.0 tree): read p64 98
  ns/chunk (parity with phase 2; hand-rolled 5.7 us/stream),
  layout_build 180 ns (was ~1.2 us — the phase-4 owned-doc cache
  removed the per-build re-parse, ~7x), sequential_reader_new 15.7
  ns, write p64 -3%, engine_compile unchanged (meta-schema
  validation dominates). No dedicated validate_bytes-stream bench:
  the phase-7 spot check (~0.2 us plan-validate vs ~0.6 us
  compile-per-call) stands; a dedicated bench is a follow-up if
  alkcall profiling motivates it.
- Downstream: alktty compiles against the path dep unchanged; alkcall
  has no dependency yet.

Verification (full block, all green): 474 tests; clippy -D warnings
clean; cargo doc zero warnings; wasm32 release build green; cargo
publish --dry-run clean at 0.3.0.
2026-09-02 09:24:30 +00:00
glm-5.3-flash 537a2170fb Fingerprint ReadPlan/OffsetMap: Hash + Eq + fingerprint() (ADR-012 §1/§4, plan phase 6)
- #[derive(Hash, Eq)] on ReadPlan, FieldPlan, CompositePlan, ReadKind,
  DiscriminatorPlan (schema: Arc<Value> hashes via serde_json Value
  Hash + Eq under preserve_order), and on OffsetMap (+ Clone;
  LeafMeta/OffsetEntry/ByteRange payload already Hash from phase 5 /
  this phase).
- fingerprint() -> u64 on both via std DefaultHasher (deferred
  decision 3 resolved: no new dep, not hot, cross-version stability a
  non-goal per ADR-012).
- Contract tests both sides: equal schemas -> equal PartialEq +
  fingerprint; field-kind / field-order / endianness changes each
  break equality and fingerprint; different root names over the same
  document fingerprint differently (ReadPlan).
- ValidationPlan already carries its own Hash/Eq/fingerprint +
  contract test (phase 7 landed early).

Verification: 474 tests pass (9 new fingerprint contract tests);
clippy -D warnings clean; cargo doc zero warnings; wasm32 release
build green.
2026-09-02 09:16:11 +00:00
glm-5.3-flash 255c8c493e OffsetMap carries LeafMeta; read/write_field dispatch on it (ADR-012 §2b, plan phase 5)
Prerequisite (review #005 M2): Hash added to Endian/VariableEncoding
derives (additive; fieldless Eq enums), and to ByteRange.

- New public types LeafMeta { kind, encoding, endian } (Copy + Eq +
  Hash) and OffsetEntry { range, meta } (start()/end() accessors),
  re-exported from lib.rs. Deferred decision 2 resolved: struct —
  get(path) -> Option<&OffsetEntry>, iter() -> (&str, &OffsetEntry).
  Storage: Vec<(String, OffsetEntry)>.
- LeafMeta computed at compute time with effective endian threaded
  through the aligned walk (container default -> field override,
  propagated into nested-struct probes and array elements via the
  referring field, matching the aligned materializer).
- engine read_field/write_field dispatch on the entry's LeafMeta:
  the per-access BastDoc re-parse + lookup_leaf_field walk +
  LeafFieldInfo are gone — the last two review #004 M1 sites.
- Parity note: lookup_leaf_field computed nested-struct defaults from
  the nested struct's own endian annotation; the map now agrees with
  the aligned materializer and packed ReadPlan (referring-field
  propagation). The old divergence (nested struct declaring endian
  under a field that also declares one) is closed; no test pinned it.
- Behavior change: read_field on a map-absent path (whole-struct
  field) errors Offset ("field not found") instead of Access
  ("composite types"); the composite-path test accepted either.
- materialize_aligned's four offset_map.get call sites updated to
  .range.start. alktty/alkcall untouched (bench never uses
  OffsetMap::get; alkcall has no dependency yet).

Verification: 465 tests pass (offset_map tests updated to the
OffsetEntry shape with per-kind LeafMeta expectations; engine test
for the old lookup walk rewritten to assert map entries carry the
LeafMeta); clippy -D warnings clean; cargo doc zero warnings; wasm32
release build green.
2026-09-02 09:13:48 +00:00
glm-5.3-flash b7c7dbe2a1 LayoutBuilder caches the owned BastDoc (ADR-012 §2a, plan phase 4)
The builder stores doc: BastDoc + endian (the doc_value: Value +
root_name: String cache is gone); new parses the typed tree once and
build walks &self.doc — the per-build BastDoc::new re-parse
(layout_builder.rs M1) is retired.

- build's root-is-struct re-check replaces its unreachable!() with a
  clean Schema error (AGENTS.md §3 never-panic; invariant unchanged —
  new already rejects non-struct roots).
- Boxing fallout: the builder now holds the full owned tree, so
  Layout::Packed boxes it (Box<LayoutBuilder>) to keep the engine's
  Layout enum variant sizes balanced (clippy large_enum_variant).
  layout_builder() still returns Option<&LayoutBuilder> via
  auto-deref; public API unchanged.

Verification: 465 tests pass unchanged (layout_builder.rs suites
drive new/build through the public API); clippy -D warnings clean;
wasm32 release build green.
2026-09-02 08:59:13 +00:00
glm-5.3-flash c583762352 Make BastDoc owned: drop Bast* lifetimes (ADR-012 §2a, plan phase 3)
Every Bast* type drops <'a>: &'a str -> String, &'a Value -> Value
(deferred decision 1: plain String/Value — the tree is built once;
Arc<str> name-sharing needs a bench justification that doesn't exist).
BastDoc::new(&Value, &str) still takes references in and clones into
owned storage; the doc gains Clone. resolve_ref/resolve_typeref/
resolve_typeref_as_def return owned types.

- Engine ownership flip: AlkTypeEngine holds the owned BastDoc
  (replacing bast_doc: Value + root_name: String; root_name()
  delegates to the doc), killing its three per-call BastDoc::new
  re-parses (aligned validate_bytes, read_field, write_field — the
  review #004 M1 pattern removed by construction; phase 5 retires the
  lookup_leaf_field walk itself). New public accessor root_name()
  (additive). Engine Send + Sync with the owned doc, asserted in the
  existing thread-share test.
- Bonus cleanup: materialize_typeref_packed's dead _field param
  dropped (phase 2 left it dangling). Under ownership, keeping it
  would force a deep Value clone per array element / record value /
  union variant via dummy_field_for. The param, dummy_field_for, and
  ty_source are gone; no behavior change (the arg was already
  ignored). BastField::synthetic keeps an owned-signature
  #[allow(dead_code)] definition (no remaining callers today).
- Consumers adapted: OffsetMap::compute(&BastDoc),
  materialize_aligned(&BastDoc, ...) (no lifetime), BuildCtx/
  ComputeCtx hold &'d BastDoc, tunion/discriminator name borrows,
  lib.rs module doc. LayoutBuilder's doc_value re-parse cache is
  unchanged pending phase 4.

Verification: 465 tests pass with zero test-logic changes (bast.rs
suites exercise every parser path through the public API); clippy
-D warnings clean; cargo doc zero warnings; wasm32 release build
green.
2026-09-02 08:31:58 +00:00
glm-5.3-flash 1641dab505 Document session-continuity rules in AGENTS.md
opencode ends the turn when an assistant message contains no tool call,
including analysis-only messages. Document the working fix (end bursts
with a tool call or a final report, land work incrementally, resume
without re-deriving) so every session inherits it.
2026-09-02 08:08:27 +00:00
glm-5.3-flash e5f1b9d825 Wire packed read path through ReadPlan (ADR-011 steps 2-4, plan phase 2)
SequentialReader now walks Arc<ReadPlan> instead of re-parsing the
BAST typed tree per field (the 400x read-path gap, review #004 H1);
materialize_packed walks the same plan, unifying the two packed
read-side consumers on one compiled form.

- SequentialReader::new(Arc<ReadPlan>) -> Self, infallible: the
  fallible BastDoc parse moved to ReadPlan::compile (phase 1). The
  reader holds the plan Arc + cursor state only; schema() returns the
  Arc<Value> retained on the plan (review #005 H2 — no
  self-referential struct); new plan() accessor exposes the shared
  plan.
- ReadPlan carries schema: Arc<Value> (set at compile; sub-plans hold
  a Null placeholder — only the root plan is handed out).
- materialize_packed(&ReadPlan, &[u8]): plan-walking packed
  materializer. The aligned path keeps walking BastDoc with the
  retained dummy_field_for/ty_source/materialize_typeref_packed
  helpers (phase 5 Scope Boundary: aligned structure walk is the
  permanent 0.3.0 design).
- Engine: Layout::Packed stores Arc<ReadPlan> alongside the builder;
  sequential_reader() is an Arc::clone (was a full-document Value
  clone); packed validate_bytes calls materialize_packed(&self.plan).
- Stride (deferred decision 4): FieldValue::Array now reports the
  true stride for fixed-size struct/nested-array elements (0.2.0
  returned 0); doc comment documents the behavioral change; no
  existing test asserted the 0, so none needed changing.
- Two parity subtleties found and preserved:
  (a) materialize_plan_composite unwraps the plan's anonymous
      single-field wrapper for primitive array elements/record values
      — without it, materialized records nest each leaf under a
      synthetic object (caught by the record parity test);
  (b) field-disc unions keep 0.2.0's materialized key order
      (__discriminator first), observable under preserve_order.
  Both are now covered by plan-phase tests or construction.

Bench (alktty wire_vs_bast, 1024 chunks/stream): packed read
2.27 us/chunk (review #004) -> 98 ns/chunk p64 / 100 ns/chunk p4k
(~23x; the 400x gap closes to ~17x vs hand-rolled 5.6 ns/chunk).
Residual gap is the per-field String allocation mandated by the
unchanged (String, FieldValue) read_next signature (2 allocs/chunk)
plus data_access bounds checks. sequential_reader() construction:
15.7 ns (was a whole-document clone).

Verification: 465 tests pass unchanged (the existing reader/
materialize/engine suites drive the rewrite through the public API —
only constructor call sites moved to ReadPlan::compile); clippy
-D warnings clean; cargo doc zero warnings; wasm32 release build
green.
2026-09-02 07:52:46 +00:00
glm-5.3-flash ff85258d03 Implement ReadPlan type + compile (ADR-011 step 1, plan phase 1)
Pure addition: the packed read-side compiled form (src/read_plan.rs)
and lib.rs wiring (module + re-exports of ReadPlan, FieldPlan,
CompositePlan, ReadKind, DiscriminatorPlan). No existing engine code
touched — phases 2-5 wire the plan into the reader/materializer/engine.

- Refined union shape (ADR-011 as refined by review #005):
  CompositePlan::Union { disc, shared, variants } with
  shared: Option<Box<ReadPlan>> for field-disc unions and
  variants: Vec<(String, CompositePlan)> — no VariantPlan/VariantKind,
  nested-union variants work by ordinary CompositePlan recursion
  (restores the 0.2.0 capability the POC rejected).
- by_name is BTreeMap (ADR-012 §1 Hash-derive prerequisite).
- True array strides (deferred decision 4): fixed struct/nested-array
  elements compute their real stride via fixed_composite_size;
  variable-length elements stay 0. 0.2.0 returned 0 for fixed struct
  arrays; that behavioral change rides the 0.3.0 bump (phase 2 will
  surface it through SequentialReader).
- Endianness: effective endian baked at every node. Parity lock: the
  plan propagates the referring field's effective endian into nested
  structs/unions — what the 0.2.0 packed reader/materializer actually
  do — and ignores nested containers' own endian annotations (the POC
  baked s.endian() there; latent divergence, never exercised by its
  equivalence tests). Nested-annotation tests lock this in.
- Untrusted input: compile carries its own depth cap (128) +
  definition-level cycle set (mirrors ValidationPlan::compile), so
  standalone compile is safe on adversarial docs: cyclic refs, deep
  chains, dangling refs, non-struct roots, and non-struct/union
  variants all surface as AlkTypeError::Schema, never a panic.
  Overflow-safe stride arithmetic (checked_mul).

Verification: 388 tests pass (355 existing + 33 new: every BastType
arm coverage, field-disc shared/nested-union compile shape, stride
computation, endian parity, cycle/depth/malformed rejection,
Send + Sync static-bound assertion); clippy -D warnings clean;
cargo doc zero warnings; wasm32-unknown-unknown release build green.

Next: phase 2 (SequentialReader + materialize_packed consume the plan).
2026-09-02 07:19:54 +00:00
glm-5.3-flashandopencode e4636e6a44 Implement ValidationPlan (ADR-012 §3, plan phase 7)
The compiled value-domain validation form: replaces the interpretive
BastDoc walk in validate_bytes with a compile-once-walk-many
constraint tree built at engine-compile time. This was the design
session + implementation ADR-012 §3 delegated; the shape decisions
are recorded in new ADR-012 §3a.

- New src/validation_plan.rs: ValidationPlan + ValidNode/ValidField/
  ValidVariant (Debug+Clone+PartialEq+Eq+Hash+Send+Sync),
  compile(&BastDoc) with eager $ref resolution, and a per-buffer walk
  with deferred error-path rendering (zero happy-path allocation,
  byte-identical error messages vs the 0.2.0 walker).
  fingerprint() via DefaultHasher, same as the phase-6 pattern.
- Compile-time graph safety: definition-level cycle set + depth cap
  (128) reject cyclic $ref graphs with AlkTypeError::Schema. The
  interpretive walker resolved refs lazily with no guard (stack-
  overflow hazard); diamond (shared) refs still compile.
- bast_validation.rs: interpretive walker retired (deleted);
  validate_value survives as a one-shot wrapper (compile + validate)
  for callers holding a doc without an engine.
- engine: Arc<ValidationPlan> built at compile in BOTH modes; the
  plan compile runs before the layout build and doubles as the
  engine's cyclic-ref gate (LayoutBuilder/OffsetMap struct recursion
  has no cycle guard; a cyclic doc previously overflowed there).
  validate_bytes walks the plan; new accessor validation_plan().
  validate_bytes signature unchanged.
- lib.rs: pub mod validation_plan + re-exports (ValidationPlan,
  ValidNode, ValidField, ValidVariant).

Verification: cargo test --release (355 pass, incl. parity suite,
fingerprint contract, cycle/depth rejection, Send+Sync + thread-share
assertions); clippy --all-targets -D warnings clean; cargo doc
zero warnings; wasm32-unknown-unknown release build green.

Co-authored-by: opencode <noreply@alk.dev>
2026-08-31 17:45:46 +00:00
glm-5.2 e461f01c97 Resolve review #005: refine 0.3.0 plan + ADR-011/012
Resolve all 11 findings from the 0.3.0 plan review (#005) in one
docs-only pass. No source changes; the crate still builds/tests at
v0.2.0. The one substantive decision change is M3 (per user
direction: ship ValidationPlan in 0.3.0, no more hedging); the rest
are spec corrections or pre-implementation refinements to types that
do not yet exist on main.

- H1: refine ADR-011 CompositePlan::Union to carry
  shared: Option<Box<ReadPlan>> (field-disc shared fields) and
  variants: Vec<(String, CompositePlan)> (drop VariantPlan/
  VariantKind). Plan phase 1 implements the refined shape.
- H2: plan phase 2 specifies ReadPlan stores schema: Arc<Value>
  (not &Value), avoiding the self-referential struct ADR-011
  rejects. Verified serde_json::Value: Hash + Eq holds with
  preserve_order, so phase 6 derives are not blocked.
- M1: nested-union support falls out of the H1 shape refinement
  (a variant can be CompositePlan::Union) — option (a) from the
  review, no behavioral drop vs 0.2.0, no Semver regression row.
- M2: plan phase 5 adds an explicit first sub-step to derive Hash
  on Endian and VariableEncoding in src/schema.rs (additive,
  semver-safe prerequisite the original plan omitted).
- M3: reverse the ValidationPlan deferral. ADR-012's "Deferring
  ValidationPlan" becomes "ValidationPlan — in scope for 0.3.0";
  new ADR-012 §3 commits the decision (compiled form, no per-buffer
  BastDoc walk, Hash + Eq + fingerprint()) and defers only the
  concrete shape to a follow-on design session + the plan's new
  phase 7. Plan gains phase 7 (ValidationPlan); old phase 7 (bump)
  renumbered to phase 8. ADR-011's Out-of-scope and Scope
  Boundaries bullets updated to point at ADR-012 §3. The deferral
  black hole this review's methodology flagged is closed: the work
  is committed with a concrete reactivation trigger, not hedged
  into an unplanned future.
- L1: plan phase 2 corrects the dummy_field_for/ty_source removal
  claim — only packed-side call sites go away; the helpers stay
  for the aligned materialize_leaf_at path.
- L2: plan phase 2 states the packed-vs-aligned
  materialize_typeref_packed split (packed gets a new plan-walking
  function; the existing function stays for aligned).
- L3: plan phase 5 adds a Scope Boundary note — aligned
  materialize's BastDoc structure walk is the permanent 0.3.0
  design; an AlignedPlan is out of scope, tracked as an OQ.
- N1: fix "back-comat" -> "back-compat" typo.
- N2: plan phase 1 verification adds the read_plan_is_send_sync
  static-bound assertion test ADR-011 requires.
- N3: Semver Contract table notes the Result drop on
  SequentialReader::new (Result<Self, AlkTypeError> -> Self)
  alongside the argument-type change.

Also: ADR-012 title -> "Plan Fingerprinting, ValidationPlan, and
Closing the Deferred M1 Sites in 0.3.0"; §3 (Fingerprinting
OffsetMap) renumbered to §4; README ADR table updated; review #005
gets a Resolution section recording how each finding was closed.

Verification (docs-only change, v0.2.0 unchanged):
  cargo test --release                     ok (310 crate + 86 integration + 2 doctests)
  cargo clippy --all-targets -- -D warnings  ok
  cargo doc --no-deps                      ok
2026-08-20 06:46:43 +00:00
glm-5.2 0e7921a02a Add review #005: 0.3.0 plan review
Cross-checks docs/plans/030-compiled-forms.md against the codebase,
ADRs 011/012, the POC on readplan-poc, and review #004.

Findings:
- H1: field-disc union shape is in neither ADR-011 nor the POC
- H2: schema() &Value on Arc<ReadPlan> is the self-referential
  pattern ADR-011 rejects
- M1: nested-union silent behavioral drop (POC rejects what 0.2.0
  accepts); deferral-black-hole pattern
- M2: Endian/VariableEncoding missing Hash derive (phases 5/6 break)
- M3: ValidationPlan deferral flagged for re-evaluation — the
  read+validate-on-untrusted-input case may be hotter than
  ADR-012's 'not a hot loop' dismissal accounts for
- L1/L2/L3: dummy_field_for wording, materialize_packed split,
  materialize_aligned BastDoc walk silence
- N1/N2/N3: typo, Send+Sync assertion test, Result drop on new

Includes a deferral-pattern scan methodology section surfacing
M1/L3/H2 as black-hole instances and confirming the plan's four
explicit deferred decisions are the healthy pattern.

Verification: file-only change, no code touched.
2026-08-20 06:09:57 +00:00
glm-5.2 2310f6cbd8 Propose ADR-012 + 0.3.0 implementation plan
ADR-012 bundles two pieces of work into the 0.3.0 release so the
crate ships one round of breaking changes, not two:

- Fingerprinting: #[derive(Hash, Eq)] + fingerprint() -> u64 on
  ReadPlan and OffsetMap. BTreeMap for ReadPlan.by_name (HashMap
  blocks Hash derive). Fingerprint contract: equal hashes => identical
  reads over identical bytes. Enables cross-run plan caching, alkcall
  hub/spoke schema handshake, schema-version diagnostics.
- Closing the deferred M1 sites via owned BastDoc (lifetime removal,
  scoped to LayoutBuilder/bast_validation/materialize_aligned/
  OffsetMap::compute) + extending OffsetMap with LeafMeta
  {kind, encoding, endian} for the aligned read_field/write_field paths.

Reframes the 'WritePlan' candidate from ADR-011's Future capabilities
section: the packed write-side compiled form is PackedLayout; the
aligned R/W compiled form is OffsetMap; the M1 fixes are 'cache the
parse' and 'extend the compiled form with leaf metadata', not 'add a
third compiled form.' Serves minimal-public-API-changes better than
a literal WritePlan type. ValidationPlan deferred (different shape,
not a hot loop).

The plan (docs/plans/030-compiled-forms.md) is the execution entry
point: seven phases ordered by dependency, each phase a session
boundary. Phase 1-2: ReadPlan (ADR-011). Phase 3: owned BastDoc.
Phase 4: LayoutBuilder M1 fix. Phase 5: OffsetMap LeafMeta. Phase 6:
fingerprinting. Phase 7: version bump + docs + verification. Includes
a semver contract table, deferred decisions, cross-phase invariants,
and the verification block.

ADR-011's Future capabilities section updated to point at ADR-012 for
the items moving into 0.3.0 and record the WritePlan reframe. README
ADR table gets ADR-012 as Proposed.

Verification: docs-only change; cargo test --release, cargo clippy
--all-targets -- -D warnings, cargo doc --no-deps unchanged (no source
touched).
2026-08-19 08:06:55 +00:00
glm-5.2 1037e68091 Accept ADR-011: compiled read plan for packed mode
Tighten framing per pre-acceptance review (no decision changes):

- Be precise about M1 coverage: closes the packed-side site
  (engine.rs:284 validate_bytes); the aligned-side M1 sites
  (engine.rs:334,467, layout_builder.rs:190) are a deliberate
  reversible bet, not a non-issue.
- Replace the 'two type walkers is symmetric with OffsetMap/
  PackedLayout' spin with an honest 'parallel typed tree, permanent
  maintenance tax, justified by ~20-50x composite-dispatch win on
  SFTP-shaped union-with-$ref-variants packets.'
- Move the 'determinism enables fingerprinting/cache/handshake' future
  work out of the positives list into a dedicated 'Future
  capabilities' section — it is a forward reference, not a current win.
- Clarify bast_doc: Value is retained unconditionally (unused in
  packed mode, still needed in aligned mode); add a Send+Sync test
  note for Arc<ReadPlan> shared from the Send+Sync engine.
- Tighten the 'no format! allocations' claim to 'no resolve_typeref,
  no BastDef::parse, no JSON node access on the happy path' (error-
  path format! remains, and is not the cost being removed).
- Add a 'POC coverage' section recording that the readplan-poc branch
  walked every BastType arm and confirmed ReadPlan covers all cases,
  including the union Byte/Field split and the array variable-stride
  (element_stride = 0) case.

Status flipped Proposed -> Accepted. README ADR table updated.

Verification: docs-only change; cargo test --release, cargo clippy
--all-targets -- -D warnings, cargo doc --no-deps unchanged (no source
touched).
2026-08-18 09:32:17 +00:00
glm-5.2 3184818c08 Propose ADR-011: compiled read plan for packed mode
Review #004 measured the packed read path at ~400x slower per chunk
than a hand-rolled codec, root-caused to SequentialReader re-parsing
BastDoc::new on every field read (the 're-parse on demand' framing
from ADR-007). The write path is competitive because it has a compiled
form (PackedLayout); the read path is the only mode/side pair without
one.

ADR-011 proposes ReadPlan — the packed read-side compiled form,
symmetric to OffsetMap (aligned R/W) and PackedLayout (packed W).
Packed positions are data-dependent (variable-length fields shift
subsequent fields), so the compiled form is necessarily a read program
(a pre-resolved tree of read instructions), not a flat lookup table
like OffsetMap. ReadPlan::compile walks BastDoc once at engine
construction, resolves all $ref\s eagerly, computes effective
endianness at every node, and inlines union variants; the read loop
then indexes into a Vec, matches on ReadKind, and calls data_access
with a precomputed Endian — no BastDoc, no resolve_typeref, no JSON
node access at read time.

Scope: SequentialReader and materialize_packed consume the plan (one
walker, not two); bast_validation, LayoutBuilder, and aligned one-shot
paths stay on BastDoc (different concern, not hot loops). Includes a
7-step Recommended Order matching review #004's structure. Breaking
public-API change (0.2.0 -> 0.3.0): SequentialReader::new and
materialize_packed take ReadPlan; BastDoc and the Bast* types are
unchanged (smaller breakage than review #004's Option A).

Cross-links: ADR-007's 'Cost' section gets a Note pointing at review
#004 and ADR-011, marking the 're-parse on demand' framing as the root
cause slated for retirement (the factory decision itself is retained);
the actual Cost/doc-comment rewrite happens in the implementation
commit per ADR-011's recommended order. README ADR table updated.

Verification: docs-only change, no source touched.

Closes review #004 H1, M1 (packed side), L1, L2 (on implementation).
2026-08-18 07:51:45 +00:00
glm-5.2 51cb552715 Add review #004: read-path performance review
Traces the ~400x read-path gap (alktty wire_vs_bast bench) to
SequentialReader::read_field_at re-parsing BastDoc::new on every field
read, with the self-referential lifetime constraint as the root cause.

Findings:
- H1: per-field BastDoc::new re-parse in sequential_reader.rs:262
- M1: same re-parse in four one-shot paths (LayoutBuilder::build,
  validate_bytes, engine read_field/write_field)
- L1: dead _field_schema param + Value clones in SequentialReader
- L2: ADR-007 Cost section + engine doc comment understate the re-parse
- N1: carry-forward of review #003 N2 (no new action)

Lays out fix options: Option A (owned typed tree, recommended, closes
H1+M1, breaking), Option B (read-plan precompute, fallback, H1 only,
non-breaking), Option C (borrow-from-engine, rejected, contradicts
ADR-007).

Verification: docs-only review; alktype source unchanged.

cab4932
2026-08-17 14:18:23 +00:00
glm-5.2 cab493206c Release v0.2.0: BAST pivot
Bump version to 0.2.0, exclude AGENTS.md from the published crate, and
add a CHANGELOG.md covering the breaking BAST pivot (schema format,
compile signature, validation split) plus bug fixes vs v0.1.0.

Verification:
- cargo test --release: all tests pass
- cargo clippy --all-targets -- -D warnings: clean
- cargo publish --dry-run --allow-dirty: packages as v0.2.0, no collision
- AGENTS.md no longer in cargo package --list; CHANGELOG.md included
v0.2.0
2026-08-17 05:47:25 +00:00
glm-5.2 82fec45bc6 Sync docs to 18 BAST kinds (Timestamp removal fallout)
- README: 19 -> 18 kinds, drop the timestamp row from the kinds table,
  drop 'timestamp shape' from the validate_bytes constraint list, remove
  the residual 'upcoming alkcall crate' sentence from the crate
  independence section (alkcall exists now), fix two '19 kinds' refs in
  the documentation pointer list and schema-layer link.
- src/data_access.rs: module doc comment still said 'all 19 AlkType
  kinds' -> 18.
- bast-format.md (normative): 19 -> 18 AlkTypeKind enum variants.
- layout-engine.md: cross-reference to 'the 19 AlkType kinds' -> 18.
- ADR-BAST (bast-bast-format.md): the Decision section claimed the post-
  pivot enum has '19 unchanged' variants; now 18, with the wording
  adjusted so it no longer says 'unchanged' across the pivot.
- ADR-VAL-SPLIT: drop 'timestamp shape' from the value-domain constraint
  list and the validator-arm table row (validate_timestamp no longer
  exists).

Left as historically accurate (describe the v0.1.0 pre-pivot state):
ADR-003/004/006 Context mentions of AlkType:Timestamp, ADR-005 'engine
now has 19', and the '19 jsonschema::Keyword factories' references in
the What-is-removed sections of ADR-BAST and ADR-VAL-SPLIT.

Verification: cargo test --release (407 pass), cargo clippy --all-targets
-- -D warnings (clean), cargo doc --no-deps (clean), cargo build --target
wasm32-unknown-unknown --release (clean).
2026-08-16 09:45:01 +00:00
deepseek-v4-pro 510553d800 Remove the Timestamp kind
The Timestamp kind was a residual from an early research reference. It
was byte-identical to String everywhere (length-prefixed UTF-8) and its
only distinguishing behavior was a hand-rolled non-strict RFC 3339 check
that the docs admitted was incomplete (Feb 31 passes, seconds range
unchecked, no leap seconds). JSON-level timestamp validation is
jsonschema's job (format: date-time on the validate_json path), not
alktype's.

Removes the AlkTypeKind::Timestamp variant, its to_bast_str/from_bast_str
mapping, the builder's Schema::timestamp() constructor, the
validate_timestamp/is_rfc3339_timestamp validator arms, and the
materializer/reader/engine timestamp arms. Updates the meta-schema
primitive enum (14 -> 13), the spec docs (bast-format.md, schema-layer.md,
builder.md, validation.md, overview.md, data-access.md, README.md), and
the kind-count references (19 -> 18).

Verification: cargo test --release (407 pass), cargo clippy --all-targets
-- -D warnings (clean), cargo doc --no-deps (clean), cargo build --target
wasm32-unknown-unknown --release (clean).
2026-08-16 09:12:33 +00:00
deepseek-v4-pro 62ed009281 Resolve review #003 nits N1, N3, N4
- N1: number_from_f64 now returns AlkTypeError::Access for non-finite
  floats instead of silently materializing Value::Null. A NaN/Inf in the
  buffer surfaces as a clear access error rather than a misleading
  'expected a number' validation error.
- N3: drop the dead Value::String arm from check_bytes; the materializer
  only ever emits bytes as an array of u8. Update the validation-model
  docs to match.
- N4: fix stale ADR references in doc comments (ADR-096 -> ADR-002,
  ADR-097 -> ADR-003, ADR-098 -> ADR-004, ADR-101 -> ADR-007).

N2 (BastType::alk_kind returning Struct for ) left as documented;
every current caller resolves the ref first, so forcing Option through
the call sites is churn without benefit.

Verification: cargo test --release (411 pass), cargo clippy --all-targets
-- -D warnings (clean), cargo doc --no-deps (clean).
2026-08-16 08:59:41 +00:00
deepseek-v4-pro 230345a867 Validate BAST documents against the meta-schema at compile time
Resolves review #003 finding M3.

- Add bast_meta::validate_bast_doc and call it from AlkTypeEngine::compile
  before parsing. Malformed annotations (unknown endian/encoding strings,
  non-integer align/maxLength, missing required properties) now surface as
  AlkTypeError::Schema instead of being silently tolerated by the parser.
- Align the meta-schema TypeRef with the parser: allow inline struct/union/
  enum as TypeRefs (the parser and builder already accepted them; the
  meta-schema and spec did not). Update bast-format.md TypeRef table and
  the BastType doc comment to the seven-form vocabulary.

Verification: cargo test --release (410 pass), cargo clippy --all-targets
-- -D warnings (clean), cargo doc --no-deps (clean), cargo build --target
wasm32-unknown-unknown --release (clean).
2026-08-16 08:54:57 +00:00
deepseek-v4-pro e5c7cc1ca2 Fix offset-indirect, field-level endian, and aligned materialization
Resolves review #003 findings M1, M2, L1, and L2.

- offset-indirect (L1 + M2 arm): rework read_*_indirect to same-buffer
  absolute offsets (safetensors-style, per the metatensor model) and add
  write_*_indirect. Wire the encoding dispatch into engine.read_field/
  write_field and the aligned materializer. Previously the encoding was
  laid out but never read back.
- field-level endian override (M1): thread field.effective_endian through
  sequential_reader, engine.read_field/write_field, and
  materialize_struct_aligned. Previously a per-field endian override was
  silently ignored, misreading multi-byte values.
- aligned-mode materialization (M2): materialize fixed-size arrays via
  their vals[i] offset-map entries and maxLength reservations as
  zero-padded fixed-size slices (trailing NULs trimmed). Previously both
  read garbage or errored.
- dead endian param (L2): drop the ignored endian argument from
  materialize_packed/materialize_aligned; endianness is read from the
  root struct.

Verification: cargo test --release (404 pass), cargo clippy --all-targets
-- -D warnings (clean), cargo build --target wasm32-unknown-unknown
--release (clean), cargo llvm-cov --release (89.60% lines).
2026-08-16 08:38:26 +00:00
deepseek-v4-pro ec73440c19 Add post-BAST-pivot code review (#003)
Covers the whole crate after the BAST pivot: correctness, code smell,
panic safety, and coverage (cargo-llvm-cov). 3 Medium findings (field-
level endian override ignored, aligned-mode validate_bytes broken for
arrays/maxLength/offset-indirect, meta-schema never applied at compile
time), 2 Low (offset-indirect dead code, dead endian param), 4 Nits.
Timestamp removal recorded as a publisher decision, tracked separately.

Verification: cargo test --release (389 pass), cargo clippy --all-targets
-- -D warnings (clean), cargo llvm-cov --release (90.14% lines / 86.68%
functions).
2026-08-15 14:43:09 +00:00
glm-5.2 562284faf4 Rewrite README for BAST pivot
The README still described the v0.1.0 custom-keyword JSON Schema format
(`AlkType:*` kinds, `compile(&mut schema, mode)`, single jsonschema
validator). Rewrite it for the BAST-era shipped API:

- What-it-is table: two validation specs (bytes via BAST-native, JSON
  via consumer-provided JSON Schema) instead of one.
- Usage example: `Definitions::new().build_doc(ChunkHeader, ...)` +
  `AlkTypeEngine::compile(&doc, ChunkHeader, LayoutMode::Packed,
  None)`; added a json!-literal variant showing the BAST document
  shape.
- The 19 kinds table: lowercase BAST `kind` strings instead of
  `AlkType:*` keywords; note the enum index bounds fix.
- Two layout modes: updated `compile` signature.
- Variable-length handling: BAST field-level `encoding` annotation.
- Union discriminators: `kind: union`, lazy variant $ref
  resolution, D-BAST-005 fields requirement.
- Endianness: struct-level (was 'top-level schema').
- Validation: two validators (ADR-VAL-SPLIT), BAST-native for bytes,
  standard jsonschema for JSON; uniform AlkTypeError::Validation
  payload (D-BAST-009); enum bounds fix noted.
- New 'BAST document shape' section: $defs required, root_name
  parameter, $ref restricted to #/$defs/<name>, BAST_META_SCHEMA
  re-export.
- Untrusted input: BAST document (was 'schema'); BAST parser
  preserves the no-panic invariant.
- Documentation index: updated for the new/rewritten docs and the
  ADR-BAST / ADR-VAL-SPLIT additions.

Verified both code examples compile and pass against the shipped API
(via a throwaway integration test, since removed).
2026-08-15 14:13:34 +00:00
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
glm-5.2 54fd112fde Remove v0.1.0 custom-keyword machinery (step 8)
The BAST parser (step 3) and BAST-native validator (step 5) replaced
the v0.1.0 custom-keyword accessor layer; step 7 moved the builder to
BAST output. This step removes the now-dead code:

Removed from src/schema.rs:
- get_alktype_kind / get_alktype_kind_enum /
  get_alktype_kind_loose / get_alktype_kind_loose_enum
  (replaced by the BAST parser's kind dispatch)
- normalize_refs / inline_union_variant_refs + helpers
  (BAST refs are always #/$defs/<name>; resolution is a single
  hash lookup, variant refs resolve lazily)
- parse_encoding / parse_align / parse_max_length / parse_endian
  (bast.rs has its own BAST-property-form copies)
- parse_discriminator + DiscriminatorKind
  (replaced by bast::BastDiscriminator; builder has its own
  Discriminator enum)
- resolve_ref / resolve_ref_or_inline
  (replaced by BastDoc::lookup_def / resolve_typeref)
- FromStr impl, as_str, Endian::from_schema, ALKTYPE_PREFIX,
  BYTE_DISCRIMINATOR_TYPES, and the associated unit tests

Kept: AlkTypeKind enum + methods (type_size, natural_alignment,
is_fixed_size, needs_endian, is_composite, is_variable_length,
to_bast_str, from_bast_str), Display (now backed by to_bast_str),
Endian, VariableEncoding, U32_SIZE, DISCRIMINATOR_PATH.

src/lib.rs: dropped the 13 schema::* helper re-exports and
DiscriminatorKind from the public surface; kept Endian, AlkTypeKind,
VariableEncoding.

Doc/comment updates: bast.rs, builder.rs, engine.rs, error.rs —
removed references to the deleted functions and the AlkType:*
keyword form.

The jsonschema crate remains a dependency (validate_json path +
BAST meta-schema validation); build_validator was already repurposed
in step 6 (no custom keywords).

Verification:
- cargo test --release: 389 pass (312 lib + 77 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo doc --no-deps: clean
- cargo build --target wasm32-unknown-unknown --release: clean
2026-08-15 13:39:05 +00:00
glm-5.2 45f3336201 Builder API produces BAST JSON (step 7)
- Schema internals: flat Map<String, Value> → Repr enum distinguishing
  BAST primitives (bare TypeRef strings), BAST composites (struct/
  union/enum/array/record objects), $ref, standard JSON Schema
  objects, and raw adopted values. Annotations (endian/align/encoding/
  maxLength) stored on the Schema and placed correctly by build()
  (struct-level) or field() (field-level).

- Primitive constructors (uint32, string, bytes, etc.) now produce
  bare BAST kind strings ("uint32") instead of {"AlkType:Uint32":true}.

- struct_().field(...) produces {"kind":"struct","fields":[{name,kind,...annos}]}
  with an ordered fields array (BAST design principle #4 — no reliance
  on preserve_order for field order).

- union_() emits {"kind":"union","discriminator":{...},"mapping":{...}}
  with BAST kind strings in the discriminator ("uint8" not
  "AlkType:Uint8"). Field-name discriminator unions emit the fields
  array; byte-offset unions omit it.

- enum_of() produces {"kind":"enum","values":[...]}.

- array_of(element).count(n) produces {"kind":"array","element":...,"count":n}.
  .count() is an additive method (D-BAST-004 requires count; the
  array_of signature is unchanged per the semver contract).

- record_of(values) produces {"kind":"record","values":...}.

- object()/string_()/etc. unchanged — standard JSON Schema output.

- encoding() now stores the value on the Schema; field() extracts it
  as a field-level property. No more keyword-object duality.

- max_length() on standard types emits the standard keyword; on BAST
  types it is extracted by field() as a field-level constraint.

- Definitions::build_doc(root_name, root) — new additive method
  producing a complete BAST document with the root type inside $defs
  (where BAST requires it). The old build()/merge_into() remain for
  backward compatibility.

- All builder tests updated to expect BAST output shapes. New tests:
  field annotations, field-name union with fields, array with count,
  ref element, encoding emission, Definitions::build_doc round-trip
  through compile(), SFTP-style union compile, array/record compile.

- Module doc comments updated (builder.rs, lib.rs).

Verification:
- cargo test --release: 427 pass (350 lib + 77 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo doc --no-deps: clean (no warnings)
- cargo build --target wasm32-unknown-unknown --release: clean
2026-08-15 13:09:18 +00:00
glm-5.2 ba7f8e1bad validate_json against consumer-provided JSON Schema (step 6)
- AlkTypeEngine::compile gains a 4th param json_schema: Option<&Value>.
  When Some, a standard jsonschema::Validator is built from the
  consumer-provided JSON Schema and stored for the JSON-validation
  path. When None, validate_json returns AlkTypeError::Schema and
  is_valid_json returns false (D-BAST-007).

- validate_json / is_valid_json signatures unchanged (per semver
  contract). Behavior: they now validate against the consumer JSON
  Schema, not a custom-keyword validator built from the alktype
  schema. The BAST document is not involved in this path.

- build_validator repurposed (deferred decision #2): same signature,
  now builds a standard jsonschema::Validator with no custom keywords.
  Behavioral break, not a type break. Re-export kept.

- Removed the 19 jsonschema::Keyword implementations and the 4
  define_*_validator! macros (dead on the bytes path since step 5,
  now dead on the JSON path too). The is_rfc3339_timestamp helper
  lives on in bast_validation.rs (already copied there in step 5).

- All compile call sites updated to pass None for json_schema (the
  layout/read/write/validate_bytes tests don't need JSON validation).

- New tests: validate_json accepts/rejects against consumer JSON
  Schema, returns Schema error when no JSON Schema supplied,
  is_valid_json false when no schema, independence from BAST doc,
  malformed JSON Schema build error, nested object JSON Schema.

Verification:
- cargo test --release: 409 pass (332 lib + 77 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo build --target wasm32-unknown-unknown --release: clean
- cargo doc --no-deps: clean
2026-08-15 12:53:35 +00:00
glm-5.2 f853dafaf1 Add BAST-native validator for validate_bytes (step 5)
Replace the jsonschema custom-keyword validator on the bytes path with
a recursive walker over the BAST typed tree. The materializer already
guarantees structural correctness; the validator enforces only the
value-domain constraints expressed in the BAST document.

- New `src/bast_validation.rs`: `validate_value` dispatches on
  `BastType`, resolving `$ref`s lazily via `BastDoc::resolve_typeref`.
  Constraint arms: integer ranges (Int8..Uint64), float finiteness,
  string/bytes `maxLength` (from `BastField::max_length`), RFC 3339
  timestamp shape, enum index bounds, union variant dispatch (recurses
  into the variant, recovering OQ-008 per-variant constraints), struct
  field presence, array count, record values.
- `engine::validate_bytes` now calls `bast_validation::validate_value`
  instead of `self.validator.validate`. The `validator` field is still
  built and used by `validate_json`/`is_valid_json` (step 6 reworks
  those).
- Enum index bounds check (`idx < values.len()`) fixes the v0.1.0 dead
  constraint: the built-in `enum` keyword checked string membership, but
  the materializer emits a numeric index that never matched.
- Errors constructed via `jsonschema::ValidationError::custom` so
  `AlkTypeError::Validation` keeps its payload type uniform with the
  `validate_json` path (D-BAST-009).
- `lib.rs`: add `pub mod bast_validation;` (engine-internal, not
  re-exported in the `pub use` block) and update the module doc.

Verification:
- cargo test --release: 446 pass (369 lib + 77 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo build --target wasm32-unknown-unknown --release: clean
- cargo doc --no-deps: clean
2026-08-15 12:44:31 +00:00
glm-5.2 04573e1d86 Wire compile() to BAST document + root name (step 4)
Step 4 of the BAST pivot: the layout engines, materializer, tunion
dispatch, and engine now consume the BAST typed tree (BastDoc/
BastStruct/BastField/BastType/...) instead of walking raw JSON with
get_alktype_kind*.

Breaking changes (per the pivot plan's semver contract):
- AlkTypeEngine::compile signature:
    compile(schema: &mut Value, mode)
    -> compile(bast_doc: &Value, root_name: &str, mode)
  Drops &mut (BAST needs no in-place normalize_refs); adds required
  root_name (D-BAST-001); input is a BAST document, not a custom-keyword
  JSON Schema.
- OffsetMap::compute, LayoutBuilder::new, SequentialReader::new now take
  a BAST document (&Value) + root_name (or &BastDoc) instead of a
  v0.1.0 schema.
- tunion::read_byte_discriminator / read_field_discriminator /
  resolve_variant / discriminator_size now take &BastUnion instead of
  &Value.
- materialize::materialize_packed / materialize_aligned now take
  &BastDoc instead of &Value.

Key design points:
- The engine stores a clone of the BAST Value + root_name so
  sequential_reader() and read_field() can re-parse the typed tree on
  demand without lifetime entanglement with the caller's Value.
-  resolution is a single hash lookup via BastDoc::resolve_typeref;
  no normalize_refs, no inline_union_variant_refs.
- bast.rs gains BastField::synthetic() (pub(crate)) for constructing
  synthetic fields wrapping TypeRefs (array elements, record values,
  union variants — these aren't fields and carry no field annotations).
- The v0.1.0 schema.rs helpers and validation.rs custom-keyword
  validators remain defined (step 8 removes them). build_validator still
  runs on the BAST doc — with no AlkType:* keywords present, the custom
  factories don't trigger and jsonschema performs structural validation
  only. The validate_bytes value-constraint enforcement (maxLength, enum
  bounds) is step 5's concern (the BAST-native validator).

Tests:
- All engine, layout, materialize, tunion, and integration tests
  converted to BAST format (kind/fields vocabulary, /
  composition). Expected validation outcomes for the layout path are
  identical; the maxLength/enum-bounds validate_bytes tests are step 5's
  regression target.
- builder.rs::builder_chunk_header_compiles_in_packed_mode uses a
  hand-written BAST doc (the builder still emits v0.1.0 format; step 7
  converts it).

Verification:
- cargo test --release: 425 pass (348 lib + 77 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo build --target wasm32-unknown-unknown --release: clean
- cargo doc --no-deps: clean
2026-08-15 12:29:27 +00:00
glm-5.2 f2f9c0326c Add BAST document parser (typed tree, step 3)
- New src/bast.rs: typed surface over a BAST document — BastDoc,
  BastDef, BastDefKind, BastStruct, BastField, BastUnion,
  BastDiscriminator, BastEnum, BastType, BastRef, BastArray,
  BastRecord. Borrows from the source Value (no clone of the tree).
- $ref resolution is a single hash lookup against $defs
  (#/$defs/<name> only); union variant refs resolved lazily via
  BastDoc::resolve_typeref / resolve_ref — replaces
  normalize_refs + inline_union_variant_refs (those stay for now;
  step 8 removes them).
- Untrusted-input safe: every walk returns AlkTypeError::Schema on
  a malformed document, never panic/unwrap (AGENTS.md §3).
  Overflow-safe usize parsing via try_from (AGENTS.md §4).
- D-BAST-005 enforced: fields array only valid with field-name
  discriminators; required for them.
- Additive only: new pub mod bast + re-exports in lib.rs. No
  existing re-exports removed (those go in step 8). 47 new tests.

Verification:
- cargo test --release: 465 pass (379 lib + 86 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo doc --no-deps: clean
- cargo build --target wasm32-unknown-unknown --release: clean
2026-08-15 11:56:03 +00:00
glm-5.2 29134789a9 Embed BAST v1 meta-schema as BAST_META_SCHEMA
Step 2 of the BAST pivot. Adds the normative BAST meta-schema
(Draft 2020-12 JSON Schema) as a public serde_json::Value, embedded
at compile time and available for validating BAST document
well-formedness. Copied verbatim from docs/architecture/bast-format.md
§The Meta-Schema.

- New src/bast_meta.rs: BAST_META_SCHEMA static, built lazily via
  LazyLock (the json! macro allocates, so it can't be a const;
  parsed once, reused as &'static Value thereafter).
- src/lib.rs: pub mod bast_meta + re-export BAST_META_SCHEMA.
  Additive public surface.

The meta-schema validates structure (correct $defs shape, known
kind strings, required properties, no additional properties,
$ref restricted to #/$defs/<name>). Value-domain constraints
(maxLength, enum index bounds) are enforced by the BAST-native
validator (step 5), not this meta-schema.

Verification:
- cargo test --release: 418 tests pass (332 lib + 86 integration);
  16 new unit tests exercise the meta-schema against valid and
  invalid BAST documents (missing $defs, unknown kind, additional
  properties, byte-discriminator union, enum, array+count, record,
  $ref, malformed ref, empty enum).
- cargo clippy --all-targets -- -D warnings: clean.
- cargo build --target wasm32-unknown-unknown --release: clean
  (LazyLock + serde_json::json! macro are wasm-safe).
2026-08-15 11:37:14 +00:00
glm-5.2 66ab9d7d93 Add AlkTypeKind::to_bast_str/from_bast_str for BAST kind strings
Step 1 of the BAST pivot. Adds the lowercase-string mapping
("uint32" <-> AlkTypeKind::Uint32) that the BAST parser and
validator dispatch on (D-BAST-002).

- to_bast_str(self) -> &'static str: returns the lowercase BAST
  string for all 19 variants (14 primitives + struct/union/array/
  record/enum). Boolean -> "bool", distinct from the PascalCase
  variant name.
- from_bast_str(s) -> Result<AlkTypeKind, AlkTypeError>: inverse of
  to_bast_str; returns AlkTypeError::Schema for unknown strings.

These are additive inherent methods on the already-re-exported enum.
The existing FromStr impl (parsing the v0.1.0 "AlkType:Uint32"
keyword form) is unchanged and removed in step 8. The two surfaces
are deliberately distinct: from_bast_str rejects "AlkType:Uint32"
and FromStr rejects "uint32".

Verification:
- cargo test --release: 402 tests pass (316 lib + 86 integration);
  6 new unit tests cover both directions, round-trip, and the
  from_bast_str/from_str distinctness.
- cargo clippy --all-targets -- -D warnings: clean.
2026-08-15 11:29:34 +00:00
glm-5.2 f5f52c61e8 Decompose BAST pivot doc into normative spec + implementation plan
The bast-pivot.md research doc had grown to 1477 lines (~58KB) through
iterative editing, pushing its most actionable content (D-BAST
decisions, POC result, migration steps) past the 50KB Read tool cap.
Agents peeking at the truncated file landed in duplicated/out-of-order
sections. Decompose into three readable-sized files with distinct roles:

- docs/architecture/bast-format.md (28KB, new): the normative BAST
  format spec -- meta-schema, TypeRef, examples, validation model.
  Grounded in the POC and D-BAST-001..009. Stable and safe to write
  now; schema-layer.md/validation.md stay describing current code and
  are rewritten post-implementation (per AGENTS.md ADR-grounding rule).
- docs/plans/bast-implementation.md (31KB, new): the execution entry
  point -- ordered 10-step plan with per-step goal/files/spec-ref/
  verification, the public-API semver contract table up front as a
  scope-creep guardrail, and the ADR-sync checklist at the end. Each
  step links to the specific bast-format.md section and D-BAST anchor.
- docs/research/bast-pivot.md (28KB, trimmed): now the research record
  only -- Summary, Motivation, POC scope/result, Decisions, Risks,
  References. The normative format spec, what-changes tables,
  validator-split details, and migration steps moved to the two new
  docs; pointers added. 1155 lines removed, 216 added.
- docs/architecture/README.md: index updated to list bast-format.md
  and the two in-progress pivot docs, with notes on schema-layer.md
  and validation.md being rewritten when the pivot lands.

All three files are under the 50KB Read cap, so an implementing agent
gets the whole document in one call. Cross-reference anchors verified
to resolve. No code changes; cargo test --release (396 tests) green.

Verification: cargo test --release (310 crate + 86 integration, all pass).
2026-08-15 10:59:14 +00:00
glm-5.2 5796d1c22f 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.
2026-08-15 10:29:24 +00:00
glm-5.2 89f05850f2 Resolve OQ-BAST-001: keep Validation(ValidationError<'static>) (D-BAST-009)
Converts the open question into a closed decision. Rationale: consumer
ergonomics on the combined validate_json + validate_bytes path — one
uniform payload type means one match arm downstream. Option 2
(Validation(String)) would force validate_json to flatten its
structured errors to a String, losing information on the richer path to
accommodate the less rich one. The no_std/minimal-build angle that
option 2 was meant to enable is moot: validate_json requires jsonschema
regardless, so a bytes-only no_std build already has to give up
validate_json as a separate larger decision; dropping the type from one
error variant doesn't unlock it.

Updates Phase 1 step 5 to reference D-BAST-009 for the error
construction pattern, and rewrites POC Result observation 4 from
'deferred decision' to 'decided — see D-BAST-009'.

No semver-relevant change to the Validation variant. Doc-only.
2026-08-15 10:13:47 +00:00
glm-5.2 e77268c951 Record BAST validator POC result; track OQ-BAST-001 error-payload decision
Adds the POC Result section (POC on branch bast-validator-poc, commit
f371fe4 — 20/20 tests, full 416-test suite green, clippy/wasm/doc clean).
Hypothesis confirmed: a recursive walker over the BAST type tree fully
replaces the 19 custom keyword validators on the validate_bytes path,
recovers OQ-008 union variant dispatch, and fixes the enum-membership
dead constraint. The POC code is reference scaffolding on the branch
and is not merged to main — it is superseded by Phase 1 step 5.

Marks Phase 1 step 2 and the POC Scope section as done with pointers to
the result section.

Elevates the deferred AlkTypeError::Validation payload-shape question
to OQ-BAST-001: keep jsonschema::ValidationError<'static> (POC choice,
simplest, dependency stays) vs introduce Validation(String) (drops
jsonschema from the error type; semver-relevant public-API change).
Decision belongs to the production refactor.

Verification: doc-only change, no code touched.
2026-08-15 09:38:53 +00:00
glm-5.2 19f8162f1a Refine BAST pivot: validation model, POC scope, resolve open questions
- Rewrite Validator Split around BAST-native validator for
  validate_bytes (walks BAST, checks value-domain constraints, no
  external JSON Schema needed); validate_json uses standard
  jsonschema::Validator from consumer-provided JSON Schema
- Add targeted POC: BAST-native validator replacing 19 custom keyword
  validators on the bytes path, verified via existing test suite
- Fix meta-schema: require count on arrays (variable-element arrays
  deferred per OQ-001), add optional fields array to UnionDef for
  field-name discriminators
- Remove lying no-count array example, replace with deferred note
- Document dead enum constraint (materialized index never matches
  string-membered enum); BAST-native validator fixes it via index
  bounds check
- Resolve all 7 OQs + 3 spec gaps as D-BAST-001 through D-BAST-008
- Update Migration Path, Risks table, Engine internals to reflect
  BAST-native validator
- Clarify 'no pocs needed' was an overcorrection: layout swap needs
  no POC, but the validation model does

Verification: docs-only change, no code affected
2026-08-15 09:05:55 +00:00
deepseek-v4-pro 82bc8f29c0 Clean up BAST pivot: drop POCs, remove recursion, add spec gaps
- Remove the four proposed POCs: they were implementation smoke tests,
  not de-risking probes. The pivot is a backend swap on a proven layout
  engine; byte-identity is already proven and the layout code is
  unchanged, so there is nothing empirical left to de-risk.
- Remove the recursive TreeNode example and the recursion mention in
  design principle 2: recursion is not a binary-layout concern and the
  engine has no cycle detection.
- Add a Spec Gaps section: validate_bytes semantics after keyword
  validator removal (UnionValidator variant dispatch regression),
  arrays of variable-length elements (engine rejects them), and
  field-name discriminator unions (meta-schema cannot express them).
- Reframe the engine change as an accessor-layer refactor: the
  walkers' (kind, field list, annotations) reads change; everything
  beneath them carries over unchanged.
2026-08-15 07:46:03 +00:00
deepseek-v4-pro 37bd5d7b7d Fix BAST pivot: jsonschema remains a direct dependency
Correct the validator split section to clarify that jsonschema is
not removed — it remains the JSON Schema validator for both paths
(BAST meta-schema validation and standard JSON payload validation).
Only the custom keyword registration path is removed. Also fix the
build_validator and validate_json entries in the engine changes
table to reflect that they are repurposed, not removed.
2026-08-14 16:06:26 +00:00
deepseek-v4-pro 004d52d505 Add BAST pivot research document
Proposes replacing custom JSON Schema keywords with a standalone
kind-based JSON format (BAST) using / for composition.
Covers format design, meta-schema, engine changes, validator split,
codegen future, ABI adapter potential, migration path, 7 open
questions, and 4 proposed POCs.
2026-08-14 15:55:17 +00:00
glm-5.2 5a4ed9e8e3 Exclude internal artifacts from crates.io package
Trim the published package from 64 → 52 files (865.7KiB → 715.3KiB,
202.8KiB → 153.0KiB compressed) by excluding internal-only files:
.opencode/ agent configs, docs/reviews/, docs/research/, and
docs/sdd_process.md. Keeps docs/architecture/ ADRs and OQs, which
document the public design.

Verification:
- cargo test --release: 396 tests pass
- cargo clippy --all-targets -- -D warnings: clean
- cargo doc --no-deps: clean
- cargo build --target wasm32-unknown-unknown --release: clean
- cargo publish --dry-run --allow-dirty: clean (52 files, 153.0KiB)
2026-08-11 09:45:15 +00:00
glm-5.2 fb3a27f974 Pre-publish docs sweep: README, AGENTS.md, licenses, inline doc fixes
- Add README.md reflecting the v0.1.0 state: 19 AlkType kinds, two
  layout modes, builder + AlkTypeEngine usage example (verified to
  compile and run), validation entry points, crate independence,
  untrusted-schemas guarantee, docs pointers. Mirrors the alkvault
  README structure.
- Add AGENTS.md with alktype-specific git workflow, project
  conventions (no comments, AlkTypeError, untrusted schemas, overflow
  safety, no async, no feature flags, wasm-clean, preserve_order
  load-bearing, no unsafe), verification commands, and ADR/OQ index.
  Blocks auto-commit on semver-relevant public API changes per the
  crates.io 0.1.0 contract.
- Add LICENSE-MIT and LICENSE-APACHE (dual MIT/Apache-2.0, matching
  alkvault and the Cargo.toml license field).
- Cargo.toml: add readme, keywords, categories, rust-version = "1.85".
- Fix broken intra-doc link in builder.rs: DiscriminatorKind ->
  crate::schema::DiscriminatorKind (cargo doc now warning-free).
- N1 (review #002): document is_rfc3339_timestamp as non-strict in the
  function doc comment. Lists the specific gaps (day-of-month per
  month, seconds range, leap seconds) and points consumers needing
  strict validation to chrono/time.
- N2 (review #002): document the FieldValue::Bytes-for-Record API
  asymmetry in the FieldValue enum doc and on read_record_value.
- .opencode/agents/implementation-specialist.md: point to AGENTS.md
  for full convention details (matches the alkvault pattern).
- review #002: mark N1/N2 resolved; all 7 findings now closed.

Verification:
- cargo test --release: 396 tests pass (310 crate + 86 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo doc --no-deps: clean (no broken intra-doc link warnings)
- cargo build --target wasm32-unknown-unknown --release: clean
- cargo publish --dry-run --allow-dirty: clean
v0.1.0
2026-08-11 09:33:33 +00:00
glm-5.2 5f88bca0d8 Fix L2: replace unreachable! with Err for untrusted-schema safety
The immediate downstream consumer (alkcall) accepts schemas from
arbitrary internet peers in its hub/spoke topology. A panic is the
wrong failure mode for a malicious or unsupported schema - an Err
the caller can handle is correct.

Three sites converted:
- offset_map.rs: _ => Err(Offset { unsupported AlkType kind for
  aligned offset computation })
- layout_builder.rs: _ => Err(Offset { unsupported AlkType kind
  for packed layout computation })
- materialize.rs: _ => Err(Schema { internal: union discriminator
  type N is not a supported byte discriminator })

The materialize.rs site uses Schema (not Access) because a wrong
disc_type is a schema-authoring bug (parse_discriminator should have
caught it), not a buffer-access error. The sibling sites in
sequential_reader.rs and tunion.rs already returned Err(Schema) -
only materialize.rs was the holdout.

Note: the k if k.is_fixed_size() guard in offset_map and
layout_builder means the compiler cannot enforce exhaustiveness at
compile time. Converting _ from unreachable! to Err is the runtime
mitigation. A future refactor could list all fixed-size kinds
explicitly to restore compile-time checking. Deferred to a separate
cleanup pass.

Verified: no unreachable! remains in production code (the one
remaining hit at offset_map.rs:688 is inside a #[test] fn).

cargo test --release: 396 tests pass, 0 failures
cargo clippy --all-targets -- -D warnings: clean
cargo build --target wasm32-unknown-unknown --release: clean (prior)
2026-08-11 09:02:56 +00:00