chore(deps): bump schemars from 1.2.1 to 1.2.2 - #265
Closed
dependabot[bot] wants to merge 1677 commits into
Closed
Conversation
…ded root catch up with D2 The seeded-root block shows the rust root verbatim (cfg_attr + no-std-check pointer), with the wasm/json-gen delta stated in one sentence. The [features] ownership prose moves from one tool-owned entry to three, and the merge table gains the "Merged, not replaced" class for features.default — union, user entries first, one-way — replacing a contract sentence the union had falsified. --rust-wasm-feature documents its two reserved names with the last-write-wins reason. Ownership-class accuracy only; the no_std consumption story is D5's. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…nsumer-shaped newtype, as the alloc-mode reshape requires
The local tier caught what D2's tool-side sweep could not enumerate: the
committed hand fragments tests/custom_serialization{,_preserve} boxed
hex::FromHexError as Box<dyn Error>, an impl hex gates on ITS std feature,
which the alloc-mode reshape (default-features = false) dropped — E0277 in
core_no_wasm / core_with_wasm / emit_wasm_tests_execute. Both fragments now
carry a local HexDecodeError newtype with verbatim-delegating Debug/Display +
core::error::Error (the FromHexErrorCore pattern), NOT a re-enabled hex/std —
the fixtures model the post-reshape world, and are the in-repo exemplar of the
D5 migration note's "hand code relied on a dep's std default features" case.
The preserve twin's pre-fix pass was MASKED by cargo feature unification: its
fixture graph carries the cddl conformance-oracle dep, whose transitive hex
uses default features, re-enabling hex/std graph-wide. Fixed anyway — the pass
was a graph accident, not evidence. The class ("a fixture verdict that depends
on a sibling dep re-unifying a reshaped-away feature") is recorded in
tests/TESTING_ROADMAP.md with its coverage boundary (the coming no_std drift
gate covers the tool-product half structurally, never spliced fixture hand
code) and a lib-only-check machinery sketch behind a reopening signal.
Sweep: all 82 committed hand-code files, the full boxed-error universe (7
sites) and the serde_json/schemars/serde std-only API surfaces classified
clean — these two hex sites were the only members. The three cells plus the
preserve/emit-tests cells re-run green; check.ts fast PASS on the final tree;
doc-citation lint green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
… the spliced-hand-code contract become current state The --export-static-crate merge description gains its two asserted [features] keys (std set, default merged-union) with the in-crate-switch-only scope — the W4 changeset falsified its "asserts exactly the dependencies" framing. And tests/README's integration-harness section states the spliced hand fragments' contract where an author meets it: they are consumer-shaped code compiling against the generated crate's own dep features, a reshape delivery sweeps the committed hand-file set (the owning family no ops-table enumeration produces), and a green compile inside an oracle-carrying fixture tree is weak evidence (graph-wide feature unification masked one of the two P13-class instances) — exemplars named, class ledger cross-referenced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…l-tier gate proves it on a std-less target (D3) The seeded rust crate root has pointed consumers at <output-root>/no-std-check/Cargo.toml since D2; this makes the pointer true. Every export now writes the shim — an always-clobbered sibling of rust/ on extern-interface's exact terms (no seed, no changeset, no overlay, no prior-output read), built from Cli alone so its two files are spec-independent. It names a generated type through the dependency's crate root (proving the seed-once root still re-exports generated::*), falls back to a crate-level use under --common-import-override, stays at the output root under --package-json by absorbing the nesting into its dep path (the third LOCKSTEP reader of that rule), and carries an empty [workspace] so cargo feature unification can never vouch for it. The repo-side half is the new no_std_check gate (local tier, bun-side, standalone-invocable): three profiles generated fresh — preserve+canonical with all four @used_as_key flavors, raw-bytes exercising FromHexErrorCore and both hex:: call sites, and json-schema-export exercising the Registrar runtime — each shim checked alone on thumbv7m-none-eabi under a three-class allowed-warning contract (cdylib drop, Serialize leaf residue, rollup lines; membership asserted, presence never), plus a host cell whose MapHashBuilder -> RandomState signature is the one place a silent std-arm hasher flip can fail. Absent target: loud SKIPPED in local, hard FAIL in full. Cells ride the shared gate cache (tree hash over the whole scratch root, lockfiles generated before hashing, verdict marker no-std-check-v1, both gate-written consumer crates inside the hashed root); the target provisions itself via rust-toolchain.toml with rustup target add as the backstop. Evidence: red/green proved (E0433 in all three thumb cells with a cache MISS on an injected std construct, green on revert with identical keys); cold 12s / rerun fully cached / offline PASS with no warm-up change; the 9-test emission family incl. the seeded-root command byte-pin; per-root reached assertions in the rustfmt-stability walk; full cargo test 284+879 green on this tree. check.ts local and full remain for the main session — this gate first participates in that local run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…st-D3 full tier Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…s-derived blind spot and the reached-root design rule The gate-cache closure audit admits every scratch read on the justification that the generated tree is hashed — sound only while gate-AUTHORED inputs also live inside a hashed root, which nothing enforces mechanically. D3's near-miss (the host std-arm consumer crate drafted outside the hashed root, self-caught before landing) is ledgered in TESTING_ROADMAP with the machinery sketch (a per-gate hashed-root manifest the audit consumes) and its reopening signal (a second cache-participating gate writing gate-authored inputs). tests/README's gate-cache section states the discipline; the no_std_check section explains why a maiden in-tier run legitimately serves every cell cached. The design-rules section gains its fourth rule: a walk over registered roots must witness each root it claims to cover (Ok-else-continue reads a mistyped or never-written root as a pass), exemplar every_written_surface_is_rustfmt_stable. cddl-matrix/README's non-matrix-resident note now names no_std_check.ts beside lint_doc_citations.ts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
… emitted tests restore std for themselves (D4) Two flag interactions close out the no_std feature work: --deserialize-depth-limit output is std-only (thread_local! guard, no core/alloc equivalent). The serialization prelude now carries a not(feature="std") compile_error! composed at the EMISSION SEAM — never in static/serialization_depth_guard.rs, which any_cbor_tests include!s into this bin crate whose features have no std key. The message is the unit-pinned DEPTH_LIMIT_REQUIRES_STD const; its leading substring is LOCKSTEP-quoted by the gate and both docs pages, and the emitted no-std-check shim for such a crate explains its by-design red in its own header (manifest asserted byte-identical to the default — the flag changes what the check says, not what it depends on). --emit-tests output now compiles and runs under cargo test --no-default-features --lib: the cfg(test) module restores std for itself (extern crate std + the rust_2024 prelude glob + a use std::panic disambiguation — std::panic! and core::panic! are different macros, and the glob alone trips the future-incompatible ambiguous_panic_imports lint), with a hand-carried copy in the nested cddl_encoding_fidelity module that a non-root extern crate cannot reach. --lib is load-bearing: the cdylib crate-type links on host, and a no_std cdylib link fails before any test runs. The no_std_check gate grows from 4 to 8 cells (VERDICT_MARKER v2): a depth_limit profile whose shim cell is the gate's first expected-FAIL class (nonzero exit AND the pinned substring; cacheableCell now caches cell-success via an accept predicate) plus a host default-features PASS cell proving the refusal is confined to not(std); and an emit_tests profile whose host_test_nostd cell is the only cell anywhere that compiles the emitted test module (cfg(test) code is invisible to every dependency-direction check — proven live: removing the restore failed only that cell while its shim sibling stayed green). Red/green proved for both new classes with cache-miss evidence; revert restored all eight prior keys. Verified foreground: check.ts fast PASS; the gate standalone 8/8; the no-std-check-emit family (10), the emit_tests family (8), both deserialize_depth_limit_guards tests, the new seam pin, every_written_surface_is_rustfmt_stable, insta_orphan, lint_doc_citations, matrix_typecheck all green. Zero corpus snapshots moved (no snapshot profile carries either flag — established by enumerating ALL_PROFILES and WHOLE_PROGRAM_CASES). local/full tiers pending (main session). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…de become current-state reference (D5) output_format.mdx gains two sections. "The std feature (building without std)" states the contract — default-on std feature, default-features = false at the dependant, the three mechanisms that keep emission single-variant, what the feature actually gates (the MapHashBuilder alias; wire bytes identical either way), why it does not forward to dependency features, and the scope boundary at the wasm/json-gen crates. "Upgrading a crate generated before the no_std output" carries all eight one-time consumer-visible changes in one place: the alloc-mode dep reshapes with the boxed-FromHexError newtype quoted from tests/custom_serialization and the graph-dependent "compiles for me" caveat (cargo feature unification, with the in-repo proof shape); the unpreserved-comment churn on rewritten use lines; features.default monotonicity; the semver-major OrderedHashMap break; the linked-hash-map tombstone and its renamed-key escape hatch; the new top-level no-std-check/ artifact; the static-runtime crate's in-crate-only std switch; and the one-line seeded-root opt-in for existing crates. The shim section's opener now hands off to the feature section instead of restating it. current_capacities.mdx gains the std-gating summary (placed outside the generated q1-limitations block): --deserialize-depth-limit is std-only with the quoted compile_error!, --emit-tests is supported under --no-default-features --lib, and the wasm/json-gen crates stay std by nature with the deferral rationale stated. Both new cross-link anchors verified against the file's headings and the repo's existing slug pattern (underscores preserved, backticks/parens dropped). Verified foreground: lint_doc_citations (223 tokens, MD022 clean), project_status_headers --check, check.ts fast. local/full tiers pending (main session). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…, per-cell-verdict reality The section landed in D3's cycle describing three profiles, four cells, one global exit-0-plus-allowed-warnings verdict, and marker no-std-check-v1. D4 changed every one of those facts: five profiles (depth_limit and emit_tests added), eight cells, per-cell accept predicates — including the inverted depth_limit.shim_thumb_expect_fail cell (nonzero exit AND the pinned compile_error substring, no warning policy) and the emit_tests.host_test_nostd cargo-test cell that is the only cell anywhere compiling the emitted cfg(test) module — a cache that stores cell-success rather than cargo-exit-0, and marker no-std-check-v2 bumped on any VERDICT-logic change, not just the allowed-warning set. Cycle-5 wind-down step 3: the one stale surface found across the four-surface sweep (both flag docs, cddl-matrix docs, and the TESTING_ROADMAP entries citing the gate all read accurate against the post-D4 facts). lint_doc_citations re-run green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…-overlay pipeline, fresh-checkout target step, and the fifth design rule AGENTS.md catches up with what the no_std workstream changed structurally: the generation/ split gains no_std_check, alloc_import_inject joins the module list with its shared-tokenizer note, the comment-preservation bullet's post-overlay sequence is now prune -> injector recompute -> rustfmt across the five write surfaces, and the fresh-worktree setup list gains the thumbv7m target prerequisite (self-provisioning via rust-toolchain.toml; per-toolchain trap and the gate's skip/fail semantics stated for non-rustup environments). tests/README's review-owned design rules gain a fifth: a delivery whose own gate covers its regression classes records the coverage fact once instead of per-bug ledger entries — with the no_std_check gate as the shipped exemplar in both directions (self-caught probe catches produced no entries; the cache-closure near-miss, which no gate covered, did). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…o no enum declares a name twice
Every arm of a group choice names a variant of the ONE enum being built, but the arm loop's three
naming branches each named independently and only one of them consulted the de-duplication pool. Two
arms landing on the same name therefore emitted `pub enum Foo { A(u64), A(String) }` — Rust E0428, a
generated crate that does not compile (and, under `--wasm`, a duplicated `FooKind` variant plus
duplicate `new_a`/`as_a`). Reachable with no `@name` at all: `foo = [ x: uint // x: text ]` derives
`X` twice.
`settle_arm_variant_name` now owns that namespace for all three branches, splitting on whether the
author WROTE the name:
- an explicit `; @name` is public API of the generated crate, so it is never renamed and a second arm
spelling it is a graceful rejection naming both arms as written — the author picks, exactly as for
the struct namespace;
- a DERIVED name (member key, type, or `{rule}{index}` position) carries no authorial intent, so it
yields and takes the first free numeric suffix. A search rather than a one-shot `+2`, since the
pool holds authored names too and `Name2` could otherwise land on an arm spelled `; @name name2`.
Every arm's explicit name is reserved in a pre-pass before the loop, so which side of a colliding
authored/derived pair keeps the plain name never depends on the order the arms were written: the
authored name wins from either position. Dedup is keyed on the FINAL camel-cased name, closing a
blind spot where `; @name my_arm` and `; @name myArm` — or an authored `x` and a field `x:` — were
invisible to each other while both emitting `MyArm`/`X`.
This is a second namespace, not a generalization of the struct-ident check that landed alongside it:
an embeddable arm is inlined and registers no struct at all yet still declares a variant, and two
arms that share ONE struct by structural equality still declare TWO variants. Both shapes emitted a
repeated variant until this ran.
The type-choice path keeps its existing silent-rename behavior for a duplicated explicit `@name`
(`Mainnet`/`Mainnet2`). That divergence is deliberate — that path emits code that compiles, so
tightening it is a pure behavior change on working output — and is recorded in the roadmap's declined
section with the consumer-observable signal that would reopen it.
…s that run wrote (D6) Reverses the recorded "std = [] forwards to nothing, deliberately" decision (maintainer ruling 2026-07-29, on CML's BUG-std-feature-does-not-forward): the impossibility argument held for a STATIC change-log entry, not for a computed op — the run that emits a manifest knows every dependency it wrote, so it never names an absent one. The rule is one sentence: a dep's std feature is forwarded iff the tool ships that dep with default-features = false and the dep exposes a std feature (probed on the resolved sources: serde, serde_json, schemars, hex do; hashlink and the cbor_event fork have none to name). ops_for_rust pushes the computed features.std after its last dependencies op; the merge is union-plus-prune (merge_std_features): user forwards to present deps survive, and a <pkg>/<feat> naming an absent dep is dropped, so a flag flip converges in one pass instead of stranding yesterday's serde/std beside a tombstoned dep. Path deps forward on request: --std-forward-dep <package> takes the --rust-dep entry with default-features = false and adds <package>/std (hard error without the matching --rust-dep). Config derives it twice over — per deps edge unconditionally, and per crate for the shared runtime when the new opt-in [runtime].lib-name names its cargo package (common-import is a Rust path prefix; no package name follows from one, which is also why the tool does not read the co-owned manifest to check the match). The exported runtime crate's own specs move to alloc mode with a computed forwarding list of their own (asserts may update specs, never remove — the D0 hashlink swap precedent), so the crate the generated crates forward INTO no longer absorbs default-features = false one hop short. The no_std_check gate gains a split_config profile — a --config tree, two crates over a shared runtime — whose thumb cell is the reachability proof for the runtime's not(std) hasher arm through a real topology (verdict marker no-std-check-v3; both new cells red/green-proved with cache-miss evidence and identical-key restoration). Shim + docs text replace the interim "does not forward" contract with the computed one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…s — text-presence dedup, the full Entry surface, and hand-written spec rendering Three CML-filed bugs plus the handoff-doc corrections, in one consumer-feedback unit: - The overlay never traps a comment this run also emits (BUG-unpreserved-comment-false- positive). A cross-version regen shifts every anchor and rewrites the statements comments annotate, so both anchored self-cancels are structurally blind to it and a comment the fresh emission carries verbatim trapped as unclassified. Text-presence dedup is the anchor-free answer, in both Placeable::Comment arms — purely suppressive, no wording touched, same residual class as the trailing cancellation. Two cross-version fixtures are the suite's first (the corpus was same-version by construction — that gap and its unwitnessed remainder are ledgered with a self-clearing-trap reopening signal). - ordered_hash_map's Entry gains or_default and and_modify (BUG-entry-wrapper-api-gap), both position-preserving (hashlink's OccupiedEntry::get_mut probed no-move at source; or_default exists nowhere in the backing crate, so the wrapper's is the only spelling). The doc comment now enumerates the actual surface instead of claiming "identical EXCEPT". Two new D0-family wire pins drive both methods from decoded bytes. - Merged dep specs render like the hand-written lines beside them (BUG-manifest-dep-spec-formatting): normalize_inline_decor clears stale-position whitespace so toml_edit's position-aware defaults apply (bounded to whitespace — a decor holding a comment or newline is the user's), and a dep the tool ADDS to a pre-existing [dependencies] table carries a " # cddl-codegen" marker instead of silently inheriting the last hand section comment. Merge-path only; zero corpus snapshots moved. - The four-category migration-search recipe (REPORT-handoff-doc-corrections) is now in output_format's upgrade guide — including resolve-.entry(-by-type, since the inherent method shadows Deref — and the survey's two falsified "complete list" claims are ledgered in TESTING_ROADMAP as instances of the evidence-lost-to-tail and vocabulary-bounded-negative classes, with the committed recipe as the systematic fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…ate cell that proves it
`static/any_cbor_json.rs` holds eight nested inline `natural_any_cbor_*` serde
adapters. A file-top `use alloc::…` does not reach a nested inline module, so the
alloc-import injector deliberately skips those bodies and each module that names an
alloc type carries its own `use super::alloc::…;` by hand. The four map adapters did;
`natural_any_cbor_seq` and `natural_any_cbor_opt_seq` name `Vec` and did not — invisible
under `std` (the std prelude supplies the name), six E0425s the moment a consumer takes
the no_std path the tool advertises. They now carry `use super::alloc::vec::Vec;`,
pointing at the canonical rationale comment on `natural_any_cbor_btreemap`'s import.
The systematic half is why the fix is minimal rather than a nested-aware injector. The
no_std gate's ten pre-existing cells structurally could not see this: `any_cbor.rs` is
emitted only when the finalized IR holds `any`, and `any_cbor_json.rs` is appended to it
only under `--json-serde-derives` — both flags were already in the `json_schema` profile,
but no profile's SPEC contained `any`, so the file was outside every thumb compile. That
profile's CDDL now carries an `any_members` rule with one member of every adapter shape
(plain `any`, optional, `[* any]` seq, optional seq, `{* uint => any}` table, optional
table), which puts all eight adapters into the compile. Scope: this covers the preserve
composition; `any_cbor_non_preserve.rs` stays file-scope code owned by the injector's own
scan, a different and lower-risk class.
Red/green, in that order (the proof discipline, not a formality):
RED — cell extended, statics unfixed: `json_schema.shim_thumb: FAIL — cargo exit 101`
[4.7s], six `error[E0425]: cannot find type Vec in this scope` in
`generated/any_cbor.rs`, two in `natural_any_cbor_seq` and four in
`natural_any_cbor_opt_seq` — the consumer's filing reproduced from the gate.
GREEN — same cell, statics fixed: `json_schema.shim_thumb: PASS [4.5s]`;
`no_std_check: PASS`, 1 run / 9 cached.
VERDICT_MARKER is deliberately NOT bumped. Its own rule is "bump on any change to the
verdict logic, not to the profiles' bytes, which the tree hash covers": `classifyWarning`,
both `Accept` predicates and the cell set are untouched, and the extended CDDL changes the
generated tree, whose hash is already key material — so no cached PASS can survive that
the new bytes should invalidate. (Observed: the pre-change `json_schema` key did not
serve, and the two runs above keyed apart.)
Emitted output re-checked for rustfmt stability in both modes (preserve and non-preserve
`any_cbor.rs` generated to scratch, `rustfmt` idempotent on both) — the comment-preservation
overlay's precondition for a static file. No blessed snapshot moves: the corpus snapshots
carry only mod.rs / serialization.rs / lib.rs / Cargo.toml / extern_interface_check.rs, and
no whole-program case emits `any_cbor.rs`, so `snapshot_tests` is green unblessed (46
passed).
The ledger carries both findings in `tests/TESTING_ROADMAP.md` § Standing-system residuals:
the composition-coverage gap (a runtime file emitted on an IR predicate × a flag is reached
only by their conjunction) and the hand-pattern miss, whose reopening signal for a
nested-aware injector is a SECOND miss — a red `json_schema` cell or a consumer filing on an
alloc name inside a nested inline module, not a new adapter added with its imports correct.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
The shim's two files claim "a failure here with an otherwise unmodified generated crate is caused by hand-written additions". That claim is only reachable once the generated crate has actually been compiled, and cargo aborts at the FIRST crate that fails — so a broken third-party dependency reds the check while the crate the check is about is never built at all, and the header above the failure then reads as an accusation about code nothing has looked at. That is not hypothetical: the first consumer to take the no_std path had a red shim attributable to a dependency that does not declare `#![no_std]`, took it as a clean bill of health for everything behind it, and found six real errors in the generated runtime only after patching that dependency locally. Both emitter sites keep the existing attribution and forwarding sentences and gain a caveat paragraph carrying three facts: cargo stops at the first failure; success-shaped silence after it is not a verdict about the crate under test; and a `Checking <name>` line is the reached-test — the one thing in cargo's output that separates "compiled clean" from "never reached". The text stays spec-independent and lib-name-free, so `no_std_check_emit_is_spec_independent` and `no_std_check_emit_same_in_both_modes` hold unchanged, and `CHECK_COMMAND` plus the seeded-root header comment are untouched (no corpus snapshot moves — they carry the command, not the sentence). `docs/docs/output_format.mdx` echoes the caveat where it restates the attribution contract, and `tests/README.md`'s one-sentence statement of the same promise is qualified the same way. Blessed diff audited: all 7 `tests/no-std-check-emit/snapshots/` files change by the caveat paragraph and nothing else — 6 added lines each, `#`-prefixed in the three manifests, `//!`-prefixed in the four roots, and in the depth-limit root inserted BEFORE the FAILS-BY-DESIGN note it belongs above. `no_std_check_emit` 10 passed; `check.ts fast` PASS; the `no_std_check` gate re-ran all 10 cells cold (the shim bytes are inside every profile's hashed tree) and passed. Co-Authored-By: Claude Opus <noreply@anthropic.com>
…counts the pointers honestly Two review-caught one-liners from the D8 pass. check.ts's no_std_check registry desc (and its section banner) still said three profiles from D3; the gate has six. And alloc_import_inject's nested-module note claimed all five non-canonical hand imports point at the canonical rationale comment — in fact the two seq imports point at it, the preserve file repeats it (a cross-file pointer would not resolve), and the two remaining map imports are bare; the sentence now says so. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
… pinned at both re-exposed surfaces
`to_raw_hex`/`from_raw_hex` are trait DEFAULT methods and a `bytes` newtype's serde impls call
hex encode/decode directly, so the backing hex crate's behavior IS the consumer-facing behavior of
two shipped surfaces. A swap or version bump of that dependency rewrites both without touching a
line of generated code, and passes every round-trip and compile gate on the way through: a
round-trip only ever feeds back what the encoder produced, so the ACCEPTED INPUT GRAMMAR is
observed by nothing, and the RENDERED ERROR TEXT reaches consumer output verbatim (the hex error
is boxed into `DeserializeFailure::InvalidStructure`, whose Display arm renders it inline).
Five pins state the incumbent behavior, asserted only through `RawBytesEncoding`/`DeserializeError`
and serde — no `hex::` path is named — so they survive a swap and a swap that changes any of them
must flip the pin in its own diff:
1. `to_raw_hex` emits plain lowercase hex, no prefix; `from_raw_hex` reads it back.
2. Decoding is case-insensitive; re-encoding normalizes back to lowercase.
3. A `0x`/`0X` prefix is REJECTED at both surfaces — reported as an invalid character at index 1
on the raw-bytes side, as serde's "invalid hex bytes" on the JSON side (the emitted
deserializer discards the hex error, so only the verdict there is the dependency's).
4. The two malformed-input classes render as `Invalid character 'g' at position 2` /
`Odd number of digits` through `DeserializeError`'s Display.
5. JSON bytes-wrapper serialize emits a plain lowercase hex string.
Pin 3 is the sharp one: a more lenient hex implementation flips it from reject to accept, widening
the accepted input grammar of a shipped public surface while serialization keeps emitting the
unprefixed form — an asymmetry no existing test can see.
No twin in tests/raw-bytes-preserve: `raw_bytes_encoding.rs` is concatenated into the composed
`serialization.rs` unconditionally of `--preserve-encodings` (generation/export.rs), so the
preserve profile runs these identical trait defaults and a duplicate suite would pin nothing new.
Each pin was proven live by mutation before landing: flipping pin 3's expected casing and
replacing the JSON prefix rejection with an `is_ok()` assertion turned `raw_bytes` and `json` red
inside the generated crates, with the observed values reading back exactly the incumbent texts.
Green: tests::integration_tests::{raw_bytes,raw_bytes_preserve,json,json_preserve};
`check.ts fast` PASS.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
… the impl it supplies is orphan-blocked for everyone
The newtype exists because neither `hex` nor `core` is ours to extend: `hex`'s `Error` impl is
`#[cfg(feature = "std")]` and it ships no `core::error::Error` impl, so a `hex::FromHexError`
cannot be boxed as `Box<dyn core::error::Error>` without a local wrapper. That wall is the ORPHAN
RULE, and it stands in front of every crate, not just this one — a hand-written sibling crate
around the generated one that stores a `hex::FromHexError` (a `thiserror` enum with a
`#[from] hex::FromHexError` arm is the usual shape) has to declare the identical wrapper. So the
runtime exports its copy rather than making each sibling duplicate it, and carries the
`From<hex::FromHexError>` that such a `#[from]` needs. The first consumer asked for exactly this.
The inner error stays private and `Debug`/`Display` keep delegating verbatim: the wrapper promises
a trait impl and a faithful rendering, not access to the inner error, and it is still deliberately
invisible in every message `DeserializeFailure` renders. `from_raw_hex` now constructs through the
`From` impl so the exported conversion has an in-tree user and one construction path.
The visibility paragraph of the doc comment is rewritten to say this, replacing wording that
justified `pub(crate)` by the type never appearing in a public signature — which remains true and
was never the reason the type could stay private.
Verified, each with its scope:
- rustfmt idempotence on the COMPOSED `generated/serialization.rs` (a raw-bytes crate generated to
scratch with an explicit `--static-dir`): a second rustfmt pass changes nothing.
- No new warnings: `cargo check --all-targets` on that crate under the pinned 1.96.1, host target,
default features, diffed against the same crate generated from the pre-change static — one
pre-existing `unused import: Serialize`, identical set. rustc does not warn on unused pub items,
and the `From` impl has a caller besides.
- `no_std_check local`: the `raw_bytes` cells went cold and re-ran green (thumbv7m-none-eabi,
no_std), which is where an out-of-class warning would have failed the cell outright.
- Snapshot impact by enumeration, not expectation: no tracked snapshot contains
`pub trait RawBytesEncoding`, `to_raw_hex` or `FromHexErrorCore`. `serialization_prelude`
snapshots the prelude with `export_raw_bytes_encoding_trait = false`, so the fragment is outside
the corpus by construction. Zero snaps moved; `check.ts fast` PASS confirms it.
- `tests::integration_tests::{raw_bytes,raw_bytes_preserve,json,json_preserve,extern_generic_raw_bytes}`
green, including the hex wire pins landed in the previous commit.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
…grammar and error-text pins Shape 5 was written from the ordered-map exemplar as a mutation-order shape. The hex wire pins landed by cycle 7 (65afc78) are the same shape on a different axis a round-trip is equally blind to: a re-exposed dependency's ACCEPTED INPUT GRAMMAR and RENDERED ERROR TEXT, which only ever see what the encoder itself produced. The section now says so and points at both pin suites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…p the grammar they always had
`hex = { package = "const-hex", version = "1.19.1", default-features = false,
features = ["alloc", "core-error"] }`. The KEY is deliberately unchanged: every
`hex::` path in the composed runtime and in the emitted code resolves through it,
so the package swap is invisible to both, and an already-written manifest needs no
tombstone. `hex/std` keeps forwarding for the same reason — cargo resolves
`<key>/<feature>` against the dependencies table, and const-hex declares a real
`std` feature (default-on), so the forward-iff rule classifies it unchanged.
`core-error` is what buys the swap: an unconditional `core::error::Error` impl on
`FromHexError`. `from_raw_hex` therefore boxes the decode error DIRECTLY, and
`FromHexErrorCore` — the local newtype that supplied that impl while hex's own was
`#[cfg(feature = "std")]` — is deleted along with its `From` impl. The newtype shape
stays written down where it is still needed: the two `custom_serialization`
fragments keep it as the worked exemplar for a dependency that ships no such impl.
const-hex's decoder accepts a leading `0x`/`0X` that hex rejected, which would have
widened the accepted input grammar of two shipped consumer-facing surfaces while the
write halves keep emitting the unprefixed form — an asymmetry no round-trip test can
see. Both decode sites therefore check for a prefix themselves: `from_raw_hex`
reconstructs the incumbent error exactly (`InvalidHexCharacter { c, index: 1 }` —
const-hex's error enum is publicly constructible and not `#[non_exhaustive]`), and
the emitted JSON bytes-wrapper deserializer returns the same serde `invalid_value` /
"invalid hex bytes" its decode-failure arm already returns, so that surface answers
one wording for one class. A bare `"0x"` is why both guards test the length: stripped
of its prefix it is a valid, empty hex string.
What moved on the wire is the rendered Display casing only, and it moves visibly here:
raw-bytes pins 3 and 4 flip `Invalid character`/`Odd number of digits` to their
lowercase spellings. Pins 1, 2, 5 and both JSON pins are untouched — the guards are
what keeps them so.
Merge-side: `package` is the first field the dep merge has had to carry, so
`the_renamed_hex_spec_merges_onto_every_pre_rename_shape` drives the production spec
onto the three shapes a consumer manifest can be in — a bare `"0.4.3"` string, the
pre-rename alloc-mode table, and a hand-migrated const-hex spec. All three converge on
a coherent const-hex entry, the version never floors at a requirement read off the old
package, and each is a fixed point. The warm-up manifest names the package too, since a
renamed dep is FETCHED by package and a key-only match would pre-fetch the wrong crate
and fail every offline cell — now drift-gated alongside version and features.
Corpus re-bless is two classes and nothing else: the hex dep line in seven manifest
snapshots, and the guard lines in eight bytes-wrapper deserializers. The raw-bytes
runtime contributes none — the corpus structurally never emits it.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
…lists name the renamed package The upgrade guide's newtype exemplar was written around `hex::FromHexError` because that was the instance that broke. The `hex` key's package now supplies a `core::error::Error` impl unconditionally, so the hex instance is gone while the problem it exemplified is not: any dependency gating its `impl Error` on its own `std` feature breaks the same boxing, and the orphan rule blocks the fix for everyone, so the newtype is a shape to write rather than a wrapper to hunt for. The example is retargeted onto a nameless `their_crate::Error`, and a new paragraph says which side of the line a dependency is on and how to check — since that is a property of the dependency, not of alloc mode. Where the guide explains the class it notes the runtime carried one of these and no longer needs to. The feature-unification warning keeps its mechanism and loses its `hex` instance for a reason worth stating rather than deleting: cargo unifies on the PACKAGE, not on the `[dependencies]` key, so the oracle's `hex` and this tool's `const-hex` can no longer unify and that specific masking cannot recur — while the warning stands unchanged for every dependency two manifests name the same way. Both forwarding lists (`--std-forward-dep`, `--export-static-crate`) and the `std` feature section now say that an entry names the KEY, which is what cargo resolves it against, and that `hex/std` therefore reaches `const-hex`'s `std`. The merge-rules category list names `package` as a tool-set field, beside `path`, so a reader who renames the key back knows it is overwritten by design. tests/README: the raw-bytes no_std profile is also the only cell that compiles `const-hex` for a bare-metal target; the hand-file paragraph states the boxed-error constraint dep-agnostically; and the wire-pin shape entry records what the swap it was written for actually did — held the grammar via an explicit guard at both decode sites, flipped loudly on the Display casing. Co-Authored-By: Claude Opus <noreply@anthropic.com>
…ne full tier Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwE3feMEtYiKw92xhJTHYY
…grammar we write
Both hex-reading surfaces — `RawBytesEncoding::from_raw_hex` and every emitted
bytes-newtype JSON deserializer — now go through a single runtime function,
`decode_canonical_hex`, whose accepted grammar is bare, even-length, LOWERCASE hex:
exactly what `hex::encode`/`to_raw_hex` emit. That equality is the point. It buys a
round-trip property on the hex ENCODING and not merely on the bytes — for every
accepted string, re-encoding the decoded bytes reproduces that string byte for byte —
which is stronger than any round-trip test over generated output could ever observe,
because those only feed back what the encoder produced. Both new property pins assert
it directly, on both surfaces.
This NARROWS the grammar by maintainer decision: uppercase and mixed-case input used
to be accepted and normalized on the way out, and is now an invalid character at the
first offending digit. A consumer holding hex from another tool lowercases it at the
call site. The `0x`/`0X` prefix stays rejected with the same verdict and the same
rendered text as before — it needs no rule of its own now, since `x` is simply outside
the canonical alphabet, so the single scan that rejects uppercase subsumes the two
inline prefix guards this replaces. The scan is ours because the backing decoder has
no strict mode: `decode` strips a prefix and takes either case, and `check_raw` takes
either case and odd length while yielding no typed error. With the scan in front,
`hex::decode` is left with exactly one reachable failure, so a doubly-malformed input
reports the character rather than the length.
The fragment is gated on `cargo_manifest::needs_hex`, promoted from a local in
`ops_for_rust` to a shared function the manifest op and the runtime composer both
call. The agreement has to be structural rather than coincidental: a fragment naming
`hex::` paths without the dep is E0433 in generated output, and the next shape added
to the predicate would otherwise have to remember two places. The
`--export-static-crate` flavor deliberately does NOT share that gate and composes the
door unconditionally — a shared runtime serves specs it has never seen, and its
manifest asserts `hex` always, so the pure-function-of-flags rule that already governs
`raw_bytes_encoding` governs this too.
The emitted JSON deserializer imports the function from
`{common_import_rust()}::serialization` and calls it unqualified (dumb push, central
prune), which is what makes it resolve in-crate, under `--common-import-override`, and
in the `--config` split alike. The runtime-table config test grew a bytes newtype for
exactly that third leg: its workspace `cargo check` compiles the import against the
shared runtime crate, and the assertion beside it pins the path so a regression to a
crate-local spelling fails with a reason instead of a bare resolution error.
Pin movement, deliberate and confined: pin 2 flips from case-insensitive-accept to
reject with its exact error text; pin 5's uppercase half flips to reject on the
verdict-only JSON surface; two rejection vectors (mixed case) and two round-trip
property pins are new. Pins 1, 3 and 4 are text-identical and were verified so. Every
Part-2 comment describing the grammar as the one this surface "always answered" is
amended here rather than left to contradict the code.
Bless: five corpus snapshots, all one class — the wrapper call site's guard lines
becoming an import plus a call. No manifest snapshot moved.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
… breaks
The accepted hex grammar was documented nowhere: the only mention was the `AnyCbor`
tagged codec's `{"bytes": …}` row, which is a DIFFERENT decoder with a laxer reader,
so anyone generalizing from it drew the wrong conclusion about the two surfaces that
matter. A new section states it once for both — `RawBytesEncoding::from_raw_hex` and
the JSON form of a `bytes` newtype — with the reject table each surface can actually
render (character and index on the raw-bytes side, verdict-only on the JSON side,
since serde's `invalid_value` carries one wording for every refusal). The
canonical-in-canonical-out property is stated as the reason rather than as a rule:
accepted grammar equals emitted grammar, therefore re-encoding an accepted string
reproduces it byte for byte. The `AnyCbor` row now says which side of that line it is
on, and why it has its own routine at all (a crate emitting `AnyCbor` need not take
the `hex` dependency).
Upgrade item 9 is the wire-facing half. It is the only entry in that list that changes
what a program ACCEPTS rather than whether it compiles, so it says who breaks (callers
feeding hex they did not get from this crate — a block-explorer paste, an
uppercase-rendering library, a non-Rust peer's JSON), the exact errors they will read,
and the one-line fix. It also says there is no flag to restore the leniency, because
the property being bought only exists while the two grammars are the same one.
tests/README: the raw-bytes no_std profile names the canonical door rather than the
prefix guard it replaced, and hand-vector shape 5's worked example is extended through
the narrowing that followed the swap. The point it now makes is the durable one — the
pins turn every grammar decision into an explicit line in a diff, whether the decision
is to hold behavior across a swap or to trade it away deliberately.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
…as everything else
One canonical hex grammar tool-wide: the `{"bytes": …}` codec's vendored reader now
accepts lowercase digits only, matching `decode_canonical_hex` on the other two hex
surfaces. Its two `char::to_digit(16)` nibble conversions become a lowercase-only
`nibble` helper, so `"A1B2"` reports the uppercase digit through the message the
routine already had rather than through a new one.
It stays a SEPARATE routine rather than calling the shared door, because the fragment
is emitted for `any`-carrying crates that need not take the `hex` dependency at all;
and it keeps its own error surface — `String` messages through serde's `Error::custom`,
odd length reported before any bad nibble. The pins therefore carry serde_json's
` at line L column C` fixup, which is what a caller actually sees.
The case pins are NEW, not flips: nothing asserted the old case-insensitivity, which
came from `to_digit`'s alphabet rather than from a decision. Enumerating the suite's
41 tests, every literal-input `from_str::<AnyCbor>` feeds a non-`bytes` key, every
other read is serialize-first through the lowercase writer, and the natural walk
strict-fails on bytes before reaching hex — so no existing test fed uppercase in.
Prefix and odd-length renderings are incumbent, pinned now that this surface's
grammar is ruled, alongside the canonical round-trip property the narrowing buys.
Pinned once in the non-preserve JSON shim, as `rendering_table_exact` already is: the
fragment is mode-independent and the preserve shim `include!`s the identical file.
No bless — the corpus never emits `any_cbor.rs`.
Co-Authored-By: Claude Opus <noreply@anthropic.com>
…sorb the cell landings query_q1_gaps --write over the 120-cell matrix (the three new containment cells moved the gap arithmetic), plus the ledger rows the last tier run re-measured. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ace ledger The two supported cells' minted vectors decode and CBOR-round-trip everywhere; their json legs fail BY CONTRACT — a bare-bstr typed key's serde image is a JSON array, so the open table's hand-written to_json errors loudly on every typed entry (OpenTableKeyImageError, the documented member-name rule). That is the existing non-string-map-key finding reaching the new construct, so the cells join decode_conformance_replay's JSON_SURFACE_SKIP citing it, the finding gains the open-table paragraph, and the ledger's stale-guard keeps the entries honest if the posture ever changes. The remedied spelling (a string-producing key serde) is what the executed open-table-json-e2e fixtures pin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ip, ruby keeps judging The rust conformance validator binds a two-row open table's minted keys against the NAMED-RULE row's arms (gap #11's wrong-rule binding, sharpened by the shape: h'00' under { * bstr => uint, * md => md } reports md's uint/text, not bstr) and rejects spec-valid bytes. The decorrelated ruby gem accepts all three rules' minted values — verified by the gate's own re-run, 178s green — so open_table joins RUST_ORACLE_SKIP citing the gap, off the list when the fork fix lands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ace ledger The corpus twin of 332bfe8: all three open_table corpus rules carry the bare-bstr typed row, so their jsonwasm replay legs hit the documented loud to_json error; ledgered against the same non-string-map-key finding, stale-guarded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e-bearing-order ledger The corpus replay's map-reordering variants (reverse_maps / everything) compare decoded values by re-encode, a premise that is FALSE by contract for @Duplicates preserve rows: both of open_table_dup's rows are PairMaps in every profile, so entry order is data — table_preserve's class (b), verbatim. The base rule and the {+ …} twin replay their reorderings cleanly on loose containers, which is the control keeping the entry honest; gate re-run green (all 90 fixtures). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… surfaces that keep it An audit of the durable doc surfaces against the session's findings, closing six gaps: The @duplicates-preserve inline-map-arm silent drop becomes captured three ways instead of zero: the first KNOWN_SILENT_DROP resident (cells 38/38b — hand-verified both comma spellings per the authoring rule, with the rest-row control isolating position), an honest-limits addendum to the docs' @Duplicates section (which prescribed a named-rule remedy that ABORTS for the recursive union — both spellings dead for the metadatum shape, now said), and a TESTING_ROADMAP deferred entry recording why neither standing net caught it and naming its observed priority signal (two CML acceptance vectors blocked). The recursive-union panic half was already ledgered — the Phase D probe rediscovered a known entry, which is the system working. The registration lessons join the walkthroughs they belong to: COMPILE_GATE_EXEMPT-before-mint in the feature-row chain (walked both ways this session — one wasted ~12 min run against none), a new ordered containment-CELL chain (walked at six fail-fast tier iterations' cost, including the reject-catalog bless, the mint-then-re-probe evidence clause, and the three replay-oracle ledgers), the bare-corpus-mint re-roll hazard beside the --only flow it contradicts, and gap #11 gains the two-row wrong-rule-binding witness the open_table skip rides. The tier-span drift that fired three times in one session becomes a build item in TESTING_ROADMAP — the interim commit-them-together discipline lived only in commit messages, which is the rot the entry replaces. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… new roadmap entry Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s own spec, not a hand ledger Both decode-conformance replay gates mutate each accept vector into spec-EQUAL re-encodings and require the decoded value to be unchanged. For the two map-reordering classes (`reverse_maps`, `everything`) that premise is false by design on an `@duplicates preserve` pair-map: entry order is part of the value in every profile, so the reordered vector is a genuinely different value. That exemption was hand-listed per (row, label) — 16 entries across the two gates, two more owed by every newly added or newly un-pinned preserve row, and a re-minted vector could make a live exemption look stale (the empty-map instance in the operational watch). It is now DERIVED from each row's own catalog `spec` by `encoding_variant_skip_kind`, so both ledgers are empty and keep only the class they can still justify by hand: a real decoder gap over a genuinely spec-equal re-encoding, cited to a cddl-matrix/ROADMAP.md finding, stale-guarded. A derived suppression carries no stale guard, because "this vector's reordering happened to be the identity" is not evidence about the row. The derivation reads the directive's POSITION, not the bare presence of the string: the directive must sit in a line's comment half and a `=>` in its code half. Both halves are load-bearing against live catalog rows. `@duplicates preserve` on a SET derives nothing (reversing map entries is the identity on an array, so a failure there would be a real finding), and `open_table.open_table` — the loose-container CONTROL whose clean reordering replay keeps the whole exemption honest — mentions the directive only in a trailing prose comment about its sibling rule, so a bare-substring rule would have deleted the control. That case is pinned against the live catalogs rather than a copied literal. Riding along, because it is the same replay-gate touch: the matrix gate's `PRESERVE_SKIP` moves to module scope as `DECODE_CONFORMANCE_PRESERVE_SKIP` and a plain `#[test]` asserts every catalog row the matrix annotations call `emission.preserve.status = "unsupported"` carries an entry — over PINNED rows too, which is the point: a pinned row is never replayed, so its ledger obligations were invisible until the distant commit that activated it. Subset-direction only, and matrix-only (the corpus catalog has no annotation axis). Red-first: with the derivation stubbed out the pair-map cases report unsuppressed; with it weakened to a bare `contains` the control row and the preserve-set cases fail; with `dsl.ignore` dropped from the ledger the new cross-check names it and says what to do. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
…ion exists
A table whose DOMAIN is a named union the cycle roots at (`u_holder = [u_val]` /
`u_val = u_map / …` / `u_map = { * u_val => u_val }`) registers before its domain
exists as a struct, so `register_rust_struct` could not name the keys-list wasm
wrapper: `name_as_wasm_array_ct` -> `directly_wasm_exposable_ct` -> `is_enum` found
the ident in neither `rust_structs` nor `generic_instances` and aborted generation
under both `--wasm` legs (the synthesis runs on the parse walk).
The mint moves rather than the guard — the assert is right to fire, since answering
`false` there would silently misclassify every genuinely-unregistered generic
instance. A domain that is not FINAL at registration defers to
`finalize_deferred_table_keys_lists` (renamed from `finalize_generic_table_keys_lists`,
which already carried the generic-collection-instance class through the same route)
and is named from the by-then-resolved domain, so mint and `keys()` accessor agree.
`table_keys_list_mint_must_defer` mirrors exactly the arms the exposability probe can
reach an `is_enum` call through, so every domain that could already be named keeps
minting in place and its emitted bytes are unchanged.
The catalog row self-retires: `recursive_union_keyed_table_nominal.cddl` flips PANIC
-> ok and becomes an ordinary regression pin. The behavioural halves are
`union_rooted_recursive_union_keyed_table_mints_its_keys_list` (both `--wasm` legs plus
the mint/reference agreement), `union_rooted_recursive_preserve_table_keeps_its_pair_map`
(the consumer's CIP-25 shape — recursion composed with a rule-position
`@duplicates preserve` still selects the `PairMap` twin), and the `ukey_`/`upres_`
blocks of the recursive-collection-ref fixture, which hold both spellings to the same
compile-and-wire floor across the default and preserve profiles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
…vectors their comment claims
The `ukey_`/`upres_` blocks arrived with a compile floor but no vectors, so the
fixture's own comment ("the same compile-and-wire floor as its collection-rooted
twin") outran what the suite asserted, and the duplicate-keyed round-trip through
the pair-map arm lived only in a scratch probe nothing re-runs.
Three vectors, all in the suite both profiles run: `[ { {1: 2}: 3 } ]` through the
union-rooted union-keyed table, asserted byte-equal to the collection-rooted twin's
emission so the rooting stays unobservable on the wire; `[ { {1: 2}: 3, {1: 2}: 4 } ]`
through the preserve table, where the duplicated KEY is itself a table — recursion in
the DOMAIN composed with the rule-position policy; and `[ [ 1 ] ]` for the array arm
that sits beside the table arm in that union, so the two container arms stay
distinguishable.
Arm assertions are `matches!` with `{ .. }` and entry counts are read off the emitted
head byte: the preserve profile carries per-variant encoding sidecars, so the same
union variant is a struct variant there and a tuple destructuring cannot serve both
profiles — the head byte is what the wire claim is about anyway, and a collapsed
container re-emitting `0xa1` is exactly what it catches.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
…olicy
`{ * k => v ; @Duplicates preserve }` in type position — a type-choice arm, a
field, an array element — built its map type without ever reading the row
entry's comment slot, so the directive was accepted and dropped and the loose
`BTreeMap` was emitted. Historical duplicate-keyed bytes then failed
`DuplicateKey` with no hint the author had asked for the container that holds
them.
`rust_type_from_type2`'s `Type2::Map` → `HomogenousMap` arm now reads that slot
the way the NAMED table's arm does, so both spellings of one shape agree on what
it means: `preserve` swaps the member to the `PairMap`/`NonEmptyPairMap`
vec-of-pairs twin, and an explicit `reject` is that policy's accepted default. A
loose table is key-unique by construction, so `reject` is deliberately not
STORED — `duplicates_reject()` reads the flag without looking at the container,
and a `Map` carrying it reads as despecialized at the WIT boundary, a
`TryFrom<Vec<(K, V)>>` door `BTreeMap` has not got.
Scope is inline shapes exactly: a named table referenced by name goes through
`Type2::Typename` and never reaches this seam, so a per-site policy cannot fork a
shared type's identity — the hazard that keeps the general member-position
`@duplicates` unsupported.
A live slot must not also be a silent-drop slot, so everything else the row entry
can carry is now refused with the spelling that works: the custom-codec pair and
its `@custom_encodings`/`@custom_wire_major` declarations through the helpers the
named rows already use (their `position` argument is now composed by the caller,
leaving those rows' pinned message texts byte-identical), and the remaining
directives through an exhaustive `RuleMetadata` destructure — a new directive
field fails to compile there until it is classified. `@name` gets its own remedy
because it has a real alternative spelling: on a type-choice arm the variant name
lives in the slot AFTER the closing brace.
A parenthesized row (`{ * (k => v) }`) is left alone: probed, the cddl AST binds a
comment written there to nothing this seam can reach, so there is no directive to
honor or to reject.
Red-first: with the parsing change reverted, the seven grid cells below fail the
sweep; `@duplicates reject`'s no-op control passes in both directions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
…braces `no_silent_directive` rendered two placements — the rule slot and an arm's trailing comment — and both sit OUTSIDE a table's braces, so the row-entry slot of an inline table was unreachable by construction and its `@duplicates` drop was invisible to the one gate whose whole job is that class. `rowEntryPlacement` renders the third: the toggled directive ends the `* k => v` line inside the braces, with the closing brace on its own line (a `}` on the comment's line would be commented out). The cell asserts the honor; red-first against the pre-fix binary it reported the silent drop verbatim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
… fixture
CML pins ~700 lines of hand-written CIP-25 serialization against two
builder-emitted vectors that walk all six capture sites a payload has. Both carry
duplicate keys inside a captured metadatum map, so until the inline table's row
carried its `@duplicates preserve` they failed `DuplicateKey` at
`MetadataDetails.Md` — red-first, captured before the directive went on.
`md`'s map arm now carries the directive on its own row, and `NOISY_V1_HEX` /
`NOISY_V2_HEX` land verbatim with their provenance: builder-assembled raw CBOR
that never went through CML's serializer, so neither side of this feature
authored them. Beside the round-trips, the structural assertions are hand-read
off the wire rather than off generator output — v1's `3 => { 1: "a", 1: "b" }`
keeps both entries under one key, and v2 keeps duplicates at BOTH levels it has
them: `5 => { "d": 1, "d": 2 }` inside the captured map (the inline table's own
pair-map) and the label `5` twice on the details rest row (the rest row's).
The header's second deviation paragraph is rewritten: neither limitation it
recorded exists now — the named spelling of a self-referential union-keyed table
generates in either rule order, and the inline arm carries the policy — so the
inline spelling here is a choice that keeps the grammar in one rule, not a
workaround. The on-chain golden stays the primary oracle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
…ot a limit The `@duplicates` section routed an inline map arm of a union to a named rule and then recorded, as "one honest limit", that writing the directive on the arm's own row was accepted and silently ignored. Both halves are gone: the named routing works for self-referential unions in either rule order, and the row-entry slot now honors the directive. So the section leads with the placement and the example that uses it, states the inline-only scope (a referenced named table takes its policy from its rule, so one shared type can never carry two stances), keeps the named spelling as the better one when a table is used twice, and enumerates what the slot refuses — the `@name`-after-the-closing-brace remedy included, spelled multi-line because a comment runs to end of line and a following `/ int` would be inside it (probed: the one-line spelling makes it a rule-position `@name` and is rejected as one). `output_format.mdx` and `current_capacities.mdx` each described the twin as a table RULE's alias target; both now say where an anonymous inline table carries it. The roadmap's deferral goes with the decision it deferred. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
…eaches
The type-choice arm is the load-bearing position: the policy rides a use-site
type with no rule of its own, so nothing can look the container up by rule ident
and the wasm side has to synthesize a wrapper class for a type the author never
named. The cell walks rust default (the arm's payload is `PairMap`, the inline
`{+ …}` field its non-empty flavor), preserve/canonical (the positional key/value
encoding sidecars a duplicate-keyed replay needs — a loose table's sidecar is
keyed and cannot), wasm (both flavored classes, and the variant's getter and
constructor naming the same one), and json+schemars (the derive over the pair-map
variant).
A scratch e2e cargo-checked all five profiles plus the json-gen crate and
round-tripped duplicate-keyed bytes at the arm position; the compiled wire
evidence of record is the CIP-25 fixture's two noisy vectors.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
…e together The two `check.ts local` runs this delivery ran re-measured the tier (median 4m7s -> 5m0s over 20 runs), so the ledger and the span it feeds land in one commit — separating them is what fail-fasts the NEXT run on `project_status_headers`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
Gate-level medians only; no derived span moved (`project_status_headers --check` reports all spans in sync), so this is the ledger write on its own. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
Probed: `tbl = { * uint => text ; @Duplicates preserve\n}` was accepted and the
directive dropped — and so was the explicit `reject`, indistinguishably, since a
dropped `reject` and an honored one produce the same bytes. The rule slot one
line over is what `register_rust_struct` reads.
That slot now rejects `@duplicates` and names the rule-slot spelling. The ruling
is deliberately the OPPOSITE of the anonymous inline table's, and the asymmetry is
the reason: an anonymous table has no rule slot, so its row is the only place its
policy can live; a named table has one, so a second honored spelling would only
let the two drift. One honored spelling per shape, in both directions.
The named seam's own comment claimed the row slot was one "that nothing reads",
which was already only half true (the custom-codec rejections read it) and is now
wrong in the other half; it says what the slot actually does. The inline seam's
comment no longer claims the two slots agree — it states where they differ and
why.
Cells 38j/38k pin both policies red-first (each failed the sweep before the
change), and 38l is the placement control: the same directive on the same rule at
the rule slot stays honored, so the rejections are attributable to position.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
…indings ledger A probe taken while scoping the inline-table duplicates seam found a generated-output defect one seam over: `@duplicates reject` on a TABLE rule under `--component` emits guest glue that `try_into()`s a `Vec<(K, V)>` into a `BTreeMap`, which has no such door — generation exits 0 with empty stderr and the crate does not compile. The root cause is one flag read without its container: `duplicates_reject()` answers about the policy, not the shape, and both WIT predicates consult it — while the sibling `wit_param_validates` already states the correct rule in its own doc comment. Ledgered with the probe (shape, flag, E0277 signature), the fix shape (make both predicates container-aware, as `is_reject_ordered_set` already is), and the two-seams context: the anonymous inline row deliberately does not store an explicit `reject`, so only the named rule slot reaches the defect today and a predicate fix lets that carve-out go. No reopening signal, deliberately: this is the ships-noncompiling-output class, so fix-or-refuse is owed now. No gate covers it — the component corpus has no reject-policy table — so the fixing commit owes that fixture. Timings ledger cell from the fast run beside it; no derived span moved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
Gate-level medians from the follow-up tier run; `project_status_headers --check` reports all spans in sync, so no derived span moves with them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
… authors look Payload-carrying union enum variants are tuple variants under the default profile and struct variants under --preserve-encodings (per-variant encoding state), so consumer code -- downstream crates or the hand fragments spliced into both export crates -- compiles against exactly one profile if it tuple-destructures. The cycle's own fixture authoring hit this as an E0164 and resolved it with braced patterns plus emitted-byte assertions; nothing stated the fact anywhere, so the knowledge lived in one test's comment. output_format.mdx states it as a current-state API fact with the profile-portable spellings, and tests/README.md turns it into fixture-authoring guidance citing tests/recursive-collection-ref/tests.rs as the worked example. No new harness: the spliced-hand-fragment layer already catches the class at authoring time, which is how this instance surfaced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC48sBjovJUro3mjzwSJ5
Bumps [schemars](https://github.com/GREsau/schemars) from 1.2.1 to 1.2.2. - [Release notes](https://github.com/GREsau/schemars/releases) - [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md) - [Commits](GREsau/schemars@v1.2.1...v1.2.2) --- updated-dependencies: - dependency-name: schemars dependency-version: 1.2.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
Contributor
Author
|
OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting If you change your mind, just re-open this PR and I'll resolve any conflicts on it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps schemars from 1.2.1 to 1.2.2.
Release notes
Sourced from schemars's releases.
Changelog
Sourced from schemars's changelog.
Commits
ed61863v1.2.2ae618c3Updatesynto v3 (#528)c072ea0docs: add missing attributes to table of contents (#517)015272fClippy fix (#512)944f727Bump bytes from 1.10.1 to 1.11.1 (#507)You can trigger a rebase of this PR by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)