Files
alktype/docs/architecture/open-questions.md
glm-5.2 c0217d91a8 Resolve v0.1.0 open questions and fix production-readiness issues
POC: /workspace/alktype-builder-poc/ (18/18 tests pass, findings in
docs/research/alktype-builder-poc/findings.md). Round 2 adds the SFTP
Packet validate_bytes tests (7 new: valid Init/Read/Write/Status,
short buffer, unknown discriminator, over-maxLength Bytes).

Open questions resolved (OQ-004 through OQ-008):
- OQ-004: Discriminator::Field name is String (already implemented;
  docs updated to mark resolved)
- OQ-005: Both union discriminator kinds return the same shape:
  {__discriminator, ...variant-fields}. Field-name path also had a
  real offset bug (returned start, not end) - fixed.
- OQ-006: builder.md Example 3 now wraps the Union in a
  Schema::struct_().field("payload", ...) and merges $defs via
  Definitions::merge_into (matches the engine's AlkType:Struct-at-root
  constraint and the SFTP wire shape)
- OQ-007: Bytes materialization is array-of-u8 (Value::Array of
  Value::Number, one entry per byte 0..=255). BytesValidator accepts
  both Value::String (validate_json) and Value::Array (validate_bytes).
  maxLength = max byte count. Replaces the lossy from_utf8_lossy path
  that corrupted non-UTF-8 bytes and broke maxLength semantics.
- OQ-008 (new): UnionValidator now dispatches to variant schemas via
  sub-validators built at factory time. AlkTypeEngine::compile calls
  schema::inline_union_variant_refs before build_validator to inline
  $refs in union mapping entries (necessary because union_factory
  receives the union node, but $defs live at the schema root).

Production-readiness fixes in src/ (no stubs/hedges in a published crate):
- materialize.rs: Record stub -> full count-prefixed key/value pair
  implementation per schema-layer.md TRecord
- materialize.rs: root_of() was broken (returned the current node, not
  the schema root) -> root schema threaded through every recursive call
  so resolve_ref_or_inline can resolve $refs for nested composites
- builder.rs: LengthPrefixed encoding setter was a no-op when the
  keyword was already in object form -> complete the branch (updates
  the encoding entry in place for both LengthPrefixed and OffsetIndirect)
- builder.rs, engine.rs: POC-referencing comments cleaned up; the
  round-trip test's or_else fallback (papering over write_field being
  aligned-only) replaced with direct byte writes

Documentation:
- builder.md: Example 3 updated; Discriminator::Field spec shows String;
  Open Questions section updated (OQ-004 resolved)
- validation.md: AlkType:Bytes and AlkType:Union validator descriptions
  updated for array-of-u8 form and variant dispatch
- open-questions.md: OQ-004/005/006/007/008 marked resolved; new
  Validation theme entries
- questions/004-008: individual OQ files updated with resolutions
- findings.md: round 2 results documented

Verification:
- cargo test: 369 -> 391 tests pass (22 new: 14 materialize, 5
  inline_union_variant_refs, 3 validation/builder)
- cargo clippy --all-targets: clean
- POC: 11 -> 18 tests (7 new SFTP Packet tests); all pass
2026-08-11 07:28:24 +00:00

9.3 KiB

status, last_updated
status last_updated
draft 2026-08-11

Open Questions

Each open question lives in its own file under questions/, named NNN-slug.md (mirroring the ADR convention). This file is the index: theme-grouped tables for scannability, plus a cross-theme Open section (active investigation targets) and a Deferred / Blocked section that surfaces the safe-exit deferrals with their blocking conditions inline — so "what's currently on the architect's desk" and "what's currently parked and why" are each answerable at a glance.

Status values:

  • open — Needs to be resolved now. Has a clear path to resolution.
  • resolved — Decided. The resolution is stated cleanly, without caveats about how it could be changed later.
  • deferred(scope) — Cannot be resolved yet. The information is genuinely missing — a crate spec, POC result, or use case that doesn't exist yet. Has a concrete blocking condition. Not a failure — scope management.
  • deferred(unclear) — Cannot be resolved yet. The pieces exist (decided in other ADRs, existing types, existing patterns) but the composition — how they fit together — isn't clear yet. Resolution requires investigation (work through examples, maybe POC), not waiting. Has a concrete investigation target and an impacts field. Not a failure — honest uncertainty in a poorly-defined problem space.
  • partially resolved — Some aspects decided, others deferred or open.
  • dissolved — The question was reframed out of existence (e.g., superseded by an ADR that retires the premise). Kept for reference.

Impacts field: Every unresolved OQ (open, deferred(scope), deferred(unclear), partially resolved) should have an Impacts field stating what it blocks downstream. Be specific: "blocks the first hub deployment because the hub dials workers" not "blocks the hub crate." This is the triage signal that makes the deferral's urgency visible.

Door type classifications describe reversal cost (how expensive it is to undo), not urgency:

  • One-way door: Reversal requires rewriting significant code or permanently closes a capability. Getting it wrong is expensive — requires ADR before implementation.
  • Two-way door: Reversal is cheap or additive. Getting it wrong is recoverable — decide, implement, revert if needed.

Door type is separate from whether a decision is made. A two-way door is a decision you make now and can revert later, not a decision to defer.

By Theme

Layout Engine

OQ Title Status Door Pri
OQ-001 Arrays of Variable-Length-Element Structs deferred(scope) two low

Platform Support

OQ Title Status Door Pri
OQ-002 no_std + alloc Support deferred(scope) two low

Schema Construction

OQ Title Status Door Pri
OQ-003 Builder API for Schema Construction resolved (ADR-009) two med
OQ-004 Discriminator::Field name — &str or String resolved two low
OQ-006 Builder spec Example 3 — wrap Union in a Struct resolved two low

Validation

OQ Title Status Door Pri
OQ-005 Union materialization shape — byte-offset vs field-name consistency resolved two med
OQ-007 Bytes materialization — lossy UTF-8 conversion resolved one med
OQ-008 UnionValidator variant dispatch resolved one med

Open

Active investigation targets — questions with a clear path to resolution that need to be worked through, not waited on. Each has a concrete investigation target. This section exists so "what's currently on the architect's desk" is answerable at a glance.

Note

: All v0.1.0 open questions (OQ-004, OQ-005, OQ-006, OQ-007, OQ-008) were resolved during the v0.1.0 POC round 2 (SFTP Packet validate_bytes POC). The resolutions are summarized below for traceability; see each OQ's full file for the decision rationale. The currently-parked OQs are OQ-001 and OQ-002 (see Deferred / Blocked below).

OQ-004: Discriminator::Field name — &str or String — RESOLVED

  • Status: resolved. String, for ownership simplicity (the builder consumes Self on setters; &str would require a lifetime parameter on Discriminator and transitively on Schema::union_). Implemented in src/builder.rs since the initial v0.1.0 builder implementation.
  • Full file: OQ-004

OQ-005: Union materialization shape — RESOLVED

  • Status: resolved. Both discriminator kinds return { "__discriminator": <value>, ...variant-fields }. The field-name path also had a real offset bug (returned start offset, not end) — fixed. Implemented in src/materialize.rs.
  • Full file: OQ-005

OQ-006: Builder spec Example 3 — wrap Union in a Struct — RESOLVED

  • Status: resolved. builder.md Example 3 now wraps the Union in a Schema::struct_().field("payload", ...) and merges $defs via Definitions::merge_into. Matches the realistic SFTP wire shape and the engine's AlkType:Struct-at-root constraint.
  • Full file: OQ-006

OQ-007: Bytes materialization — lossy UTF-8 conversion — RESOLVED

  • Status: resolved. Array of u8: the materializer produces Value::Array of Value::Number (one entry per byte, 0..=255) for AlkType:Bytes fields. The BytesValidator accepts both Value::String (for validate_json) and Value::Array (for validate_bytes). maxLength = max byte count. Implemented in src/materialize.rs and src/validation.rs.
  • Full file: OQ-007

OQ-008: UnionValidator variant dispatch — RESOLVED

  • Status: resolved. UnionValidator now builds a sub-validator for each variant at factory time and dispatches on __discriminator at validation time. AlkTypeEngine::compile calls schema::inline_union_variant_refs before build_validator to inline $refs in union mapping entries (necessary because the union_factory receives the union node, but $defs live at the schema root). Implemented in src/validation.rs, src/schema.rs, and src/engine.rs.
  • Full file: OQ-008

Deferred / Blocked

The safe-exit visibility surface. These questions are parked because the information needed to resolve them does not exist yet — each has a concrete blocking condition. They are not failures; they are scope management. This section exists so "what's currently blocking the architect" is answerable at a glance, not by filtering the tables above.

Note

: OQ-003 (Builder API) was resolved by ADR-009 in v0.1.0 and is retained below for traceability — it's marked RESOLVED, not deferred. The currently-parked OQs are OQ-001 and OQ-002.

OQ-001: Arrays of Variable-Length-Element Structs

  • Blocked on: A concrete consumer that needs arrays of structs with variable-length fields, where the elements are interleaved ([fixed_0][str_0][fixed_1][str_1]...) and the engine must walk sequentially rather than use a fixed stride. The SFTP Name packet has Vec<File> where File contains strings, but SFTP serializes this as a sequence of length-prefixed strings (the serde SeqAccess pattern), not as an array of fixed-stride structs. Arrays of fixed-size structs are fully supported.
  • Priority: low
  • Full file: OQ-001

OQ-002: no_std + alloc Support

  • Blocked on: An embedded use case that requires no_std + alloc (e.g., a microcontroller running Rust without std). The WASM target has std available via wasm-bindgen. The engine's core (offset computation, read/write) is already allocation-free; the jsonschema dependency is the only alloc consumer.
  • Priority: low
  • Full file: OQ-002

OQ-003: Builder API for Schema Construction — RESOLVED

  • Status: resolved by ADR-009 in v0.1.0.
  • Unblocking condition: alkcall — the merged alknet-call + alknet-channels extraction from @alkdev/alknet — is the concrete consumer that needs programmatic schema construction in Rust. It builds both binary-layout schemas (channels' 8-byte chunk header) and JSON payload schemas (OperationSpec input/output/error schemas) at runtime.
  • Resolution: A fluent Rust builder producing serde_json::Value, covering AlkType kinds and standard JSON Schema. Implemented in src/builder.rs; spec in builder.md. See ADR-009.
  • Full file: OQ-003