1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! Stable binary canonical-AST format for Lex programs (#206).
//!
//! The text-format `.lex` source is a debugger / new-developer
//! affordance; the canonical AST (`Vec<Stage>`) is the substrate
//! that matters. This module gives that substrate a stable wire
//! representation so agents can submit canonical AST directly to
//! `lex-bytecode::compile_program` without round-tripping through
//! the parser, and so two agents proposing the same logical change
//! produce byte-identical input.
//!
//! Encoding:
//! - byte 0: format version (currently `1`).
//! - bytes 1..: canonical-JSON bytes of the `Vec<Stage>`, using
//! the same `canon_json` rules `lex-vcs` already uses for OpId
//! content-addressing. Object keys sorted, no whitespace, UTF-8.
//!
//! Why JSON-flavored bytes instead of CBOR or postcard:
//!
//! * `lex-vcs::OpId`, `StageId`, and `SigId` already hash via
//! canonical-JSON. Reusing the same byte representation means
//! "OpId is bit-identical across runs producing the same
//! logical program from canonical-AST input" (the issue's
//! acceptance criterion) holds by construction — no separate
//! format to keep in sync.
//! * Adding a CBOR / postcard / etc. dep is deferred to a later
//! slice once a need shows up. Today the agent-emit + compile
//! round-trip works on these bytes; the format can swap behind
//! `encode_program` / `decode_program` without breaking callers.
//!
//! Versioning rules:
//!
//! Adding a new `Stage` variant or a new field to an existing
//! variant doesn't bump the version — serde's default-value
//! handling reads old bytes into the new struct. Removing or
//! renaming a field DOES bump the version. Today's version is
//! `1`; if it ever bumps, `decode_program` keeps a thin shim
//! that recognises legacy bytes and runs the appropriate
//! migration.
use crateStage;
/// Current canonical-format version. Bumped on incompatible
/// schema changes (field removal/rename); additive changes
/// (new variants, new fields with defaults) stay version-stable.
pub const CANONICAL_VERSION: u8 = 1;
/// Errors `decode_program` surfaces on malformed input.
/// Encode a program (`Vec<Stage>`) to its canonical bytes.
///
/// Round-trip property: for any two parses `a` and `b` of the same
/// `.lex` source, `encode_program(&a) == encode_program(&b)`. And for
/// any program `s`, `decode_program(&encode_program(&s))` returns
/// `Ok(s')` with `encode_program(&s') == encode_program(&s)`.
/// Decode canonical bytes back to a program. Verifies the version
/// byte before attempting deserialization so a wrong-version input
/// surfaces as a clean error instead of a confusing serde failure.