- 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
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)
Four of seven review findings resolved. 5 new tests (391 -> 396 crate
tests; 438 -> 443 total). cargo test, clippy, wasm32 all green.
M2 (data_access.rs): write_bytes now validates data_len fits in u32
before the length-prefix cast. A >4GiB blob returns Access error
instead of silently writing a truncated length prefix (silent data
corruption on read-back).
M1 (builder.rs): Definitions::merge_into rewritten to access self.defs
directly instead of round-tripping through self.build() with a double-
cloned/unwrap_or_default chain that could silently drop definitions
on a shape mismatch. 4 new tests: insert-when-absent, merge-into-
existing, overwrite-duplicate-keys, no-op-on-non-object-top.
L1 (materialize.rs): byte-offset discriminator arm of
materialize_union_packed now uses checked_add for offset+disc_offset
and disc_abs_offset+disc_size, returning Access error on overflow.
Mirrors the existing sequential_reader.rs::read_union_value pattern.
L3 (error.rs): AlkTypeError::source() now returns Some(inner) for the
Validation variant (jsonschema::ValidationError implements
std::error::Error). Existing source_returns_none_for_all_variants
test split into source_returns_none_for_schema_offset_access and
source_returns_some_for_validation_variant.
Deferred: L2 (unreachable! -> Err, defense-in-depth), N1 (non-strict
RFC 3339 validator, docs-only), N2 (FieldValue::Bytes for Record,
API asymmetry). Review doc updated with resolution section.
Full source read of all 13 src/*.rs files for correctness, panic
safety, and API ergonomics ahead of v0.1.0 crates.io publish.
Findings:
- M1: Definitions::merge_into silently drops data via double-
unwrap_or_default chain; untested public API
- M2: write_bytes truncates u32 length prefix on >4GiB data
(data_len as u32 without bounds check)
- L1: materialize.rs union path uses unchecked offset arithmetic
(sequential_reader.rs sibling uses checked_add)
- L2: three unreachable!() in production code (offset_map, layout_
builder, materialize)
- L3: AlkTypeError::source() returns None for Validation variant
(ValidationError implements std::error::Error)
- N1: is_rfc3339_timestamp is non-strict (Feb 31 passes, seconds
unchecked)
- N2: SequentialReader returns FieldValue::Bytes for Record (API
asymmetry vs other composites)
Verification baseline (commit c0217d9):
- cargo test --release: 438 tests pass (391 crate + 47 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo build --target wasm32-unknown-unknown --release: clean
- no unsafe, no TODO/FIXME, all unwrap/expect/panic in test modules
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
Four open questions were scattered inline in builder.md and the POC
findings doc. Moved them into the central OQ tracker under
docs/architecture/questions/ and updated the index:
- OQ-004: Discriminator::Field name type (&str vs String) — raised in
builder.md during ADR-009 spec drafting
- OQ-005: Union materialization shape (byte-offset vs field-name
consistency) — raised in the POC findings
- OQ-006: Builder spec Example 3 wrap Union in Struct — raised in the
POC findings (doc fix; engine requires AlkType:Struct at top level)
- OQ-007: Bytes materialization lossy UTF-8 — raised in the POC
findings (blocks SFTP use case for validate_bytes)
Index updates:
- open-questions.md: new 'Schema Construction' and 'Validation' theme
groups; new 'Open' section for active investigation targets (distinct
from 'Deferred / Blocked' which holds scope-parked OQs)
- README.md: OQ table extended with OQ-004 through OQ-007
- builder.md: inline OQ-004 replaced with a tracker reference
- findings.md: inline OQ-005/006/007 replaced with tracker references
Doc-only change; 369 tests pass.
POC: /workspace/alktype-builder-poc/ (11/11 tests pass, findings in
docs/research/alktype-builder-poc/findings.md). The POC code lives outside
the repo per the internal dev convention.
Implementation:
- src/builder.rs: Schema, Definitions, Discriminator types. Constructors
for all 19 AlkType kinds + standard JSON Schema types (object/array/
string/integer/number/boolean/null/any). Setters for ADR-003 annotations
(endian/align/encoding/max_length), composite builders (field/required/
items/mapping), and standard JSON Schema constraints (minimum/maximum/
minLength/minItems/maxItems/format/title/description). 16 unit tests.
- src/materialize.rs: materialize_packed and materialize_aligned functions
that walk a schema + buffer to produce a serde_json::Value tree. Recurses
into Struct, Array, Union (byte-offset discriminator). Record is stubbed
(deferred for the POC scope).
- src/engine.rs: AlkTypeEngine::validate_bytes(&[u8]) added (ADR-010).
Dispatches on layout mode, materializes Value, then validates against
the existing jsonschema validator. 7 unit tests.
- src/lib.rs: pub mod builder, pub mod materialize; re-exports Schema,
Definitions, Discriminator.
Verification:
- cargo test: 346 -> 369 tests pass (23 new: 16 builder, 7 validate_bytes)
- cargo clippy --all-targets -- -D warnings: clean
- POC (11 tests): builder round-trip + validate_bytes (packed + aligned) +
validate_json for call payloads; all pass
Findings:
- Top-level schema must be AlkType:Struct (existing constraint); unions are
field types within a struct. Builder spec Example 3 needs a doc fix.
- Builder field order preserved (preserve_order feature, load-bearing for
packed mode).
- validate_bytes correctly distinguishes Access (read phase) from
Validation (validate phase) errors, with field paths.
- validate_json path unchanged for call payloads.
Open questions surfaced (OQ-005, OQ-006, OQ-007) tracked in findings.md;
to resolve before the SFTP Packet POC round.
Rename all 19 JSON Schema custom keyword strings from "TypeDef:*"
to "AlkType:*" (e.g., "TypeDef:Struct" -> "AlkType:Struct")
across source, tests, and docs. This is a breaking change to the
schema format itself — existing schemas using the old keywords
must be updated.
Rename the Rust identifiers:
- TypedefEngine -> AlkTypeEngine
- TypedefError -> AlkTypeError
- TypeDefKind -> AlkTypeKind
- TYPEDEF_PREFIX -> ALKTYPE_PREFIX
- get_typedef_kind{,_loose,_loose_enum,_enum} ->
get_alktype_kind{,_loose,_loose_enum,_enum}
Update error message strings ("unknown TypeDef kind" ->
"unknown AlkType kind"), 11 test function names containing
typedef_kind/to_typedef_error, and doc-comment prose ("TypeDef
kind" -> "AlkType kind", "typedef engine" -> "alktype
engine", "typedef schema" -> "alktype schema"). Fix the broken
docs/architecture/crates/typedef/ path references in source doc
comments to point at docs/architecture/ directly. Rebrand the
typedef:annotation test fixture and the "not-a-typedef" test
string to their alktype equivalents.
Update ~20 generic "typedef" prose references in the architecture
docs ("typedef is the binary struct engine", "use typedef",
"typedef limitation", "replaced by typedef", etc.) to alktype.
Rename TypedefEngine in the ADR-007 code example to AlkTypeEngine.
Preserve as provenance per the prior prose-rebrand decision:
typedef.ts references (external TypeBox source file),
docs/research/alknet-typedef/findings.md research citations,
/workspace/alknet-typedef-poc/ POC path, and the
"alknet-typedef:" research section headers in findings.
Build, 295 tests, and clippy all pass clean.
Renumber ADRs 095-102 to 001-008 and OQs 069-071 to 001-003, and
update all cross-references (titles, body prose, file-path links,
tables) across the 5 spec docs, README, open-questions index, and
all 11 ADR/OQ files. Inline the ADR-009 door-type definition from
the parent alknet project (broken cross-project reference).
Rebrand prose: alknet-typedef -> alktype in headings, body text,
dependency diagrams, and "additions" notes. Disambiguate the prior
failed attempt at /workspace/@alkimiadev/alktype/ as "the
@alkimiadev/alktype prototype" to distinguish it from this crate.
Historical research citations (docs/research/*, /workspace/alknet-typedef-poc/)
are kept as-is for provenance.
Rename the crate in Cargo.toml ([package].name, [lib].name) and
update the 11 use alknet_typedef::* imports across the 4 test files.
Rebrand the crate-level doc comment in src/lib.rs.
The TypeDef:* keyword strings, TypedefError/TypedefEngine identifiers,
and other code-level references are unchanged — those are a separate
code rebrand pass.
Build, 295 tests, and clippy all pass clean.
Copy the binary struct engine (src/, tests/) verbatim from
alknet/crates/alknet-typedef and create a standalone Cargo.toml
(workspace-inherited fields inlined). Port the architecture docs
(specs, ADRs 095-102, OQs 069-071) from alknet's nested multi-crate
layout to a flat single-crate layout, fixing relative link paths.
Build, 295 tests, and clippy all pass clean.