Skip to content

Commit 72a8feb

Browse files
codex1: draft core semantics contract
1 parent ad05075 commit 72a8feb

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
lines changed

core-no-std-plan.md

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Eon Core / Minimal Dependency Production Plan
22

3-
Last updated: 2026-03-30
3+
Last updated: 2026-03-31
44
Branch: `add-lsp-and-zed-extension`
55
Baseline commit: `ea2f5c1`
66

@@ -91,17 +91,48 @@ Deliverables:
9191

9292
Tasks:
9393

94-
- [ ] Write a short spec for `eon_core` parse semantics
95-
- [ ] Write a short spec for `eon_core` write semantics
96-
- [ ] Define exact roundtrip guarantees
97-
- [ ] Define which legacy behaviors are compatibility-only and not core guarantees
98-
- [ ] Document unsupported or intentionally ambiguous cases
94+
- [x] Write a short spec for `eon_core` parse semantics
95+
- [x] Write a short spec for `eon_core` write semantics
96+
- [-] Define exact roundtrip guarantees
97+
- [-] Define which legacy behaviors are compatibility-only and not core guarantees
98+
- [-] Document unsupported or intentionally ambiguous cases
9999

100100
Exit criteria:
101101

102102
- every fuzz/property invariant matches a documented contract
103103
- unsupported cases are identified instead of silently drifting
104104

105+
Current semantics draft:
106+
107+
- Parse semantics:
108+
- Root shape is determined from local syntax only.
109+
- A top-level `value:` pair starts an implicit root map.
110+
- Multiple top-level values without root braces become an implicit root list.
111+
- Otherwise the document is a single root value.
112+
- On the core-backed owned `Value` path, identifiers in value position parse as unit variants, while quoted tokens parse as strings.
113+
- On owned `Value` parse paths, identifiers in map-key position canonicalize to string keys rather than identifier/variant values.
114+
- Variant payload `(` must be inline with the variant head; comments or newlines between the head and `(` are rejected on the formatter-core path.
115+
- Write semantics:
116+
- Formatter-core canonicalizes non-empty root maps without outer braces by default.
117+
- Empty root maps remain explicit because brace-less output cannot represent them.
118+
- Composite first keys do not force outer braces; callers can opt in with `always_include_outer_braces`.
119+
- Whitespace and newlines between a map key and `:` are canonicalized away.
120+
- Comments between a map key and `:` are rejected.
121+
- Comments between `:` and the value normalize to entry-prefix comments.
122+
- Simple lists and variants stay on one line when they are short and comment-free; multiline containers omit commas in canonical output.
123+
- On the compact owned `Value` core writer, unit variants may be bare only in root/value position; map-key variants stay explicit with `()`.
124+
- On the legacy owned `Value` formatter, unit variants collapse to quoted strings.
125+
- Exact roundtrip guarantees currently documented:
126+
- Formatter-core canonical output is idempotent on the tested subset of root maps/lists/values, variants, strings, comments, and composite-root-key shapes.
127+
- Exact owned `Value` roundtrip through the compact core path is expected on the tested subset except for the explicit exclusions below.
128+
- Exact owned `Value` roundtrip through the legacy formatter/parser path is expected on the tested subset except for the explicit exclusions below.
129+
- Current explicit non-guarantees and tolerated boundaries:
130+
- Exact owned legacy `Value::format` roundtrip is not guaranteed for unit variants with empty payloads; they serialize as quoted strings.
131+
- Exact owned legacy/core `Value` roundtrip is not guaranteed for `null`/`true`/`false` in map-key position; both parse paths canonicalize those keys to strings.
132+
- Byte-for-byte preservation of commas, braces, indentation, or trivia is not guaranteed.
133+
- Legacy parity is guaranteed only on the documented and tested overlapping formatter subset.
134+
- Remaining unsupported or merely tolerated ambiguous forms should be promoted into this list as fuzz/property exclusions are reviewed.
135+
105136
## WS2 - Zero-Copy Parsing
106137

107138
Status: `[-]`
@@ -195,7 +226,7 @@ Current formatter compatibility contract:
195226
- Matching legacy error messages.
196227
- Full legacy parity outside the documented and tested overlapping syntax subset.
197228
- Exact owned `Value::format` roundtrip for unit variants with empty payloads; the legacy formatter renders them as quoted strings.
198-
- Exact owned `Value::to_string_with_core` roundtrip for `null`/`true`/`false` in map-key position; the core parser canonicalizes those key-position keywords to strings.
229+
- Exact owned `Value::format` and `Value::to_string_with_core` roundtrip for `null`/`true`/`false` in map-key position; both parse paths canonicalize those key-position keywords to strings.
199230

200231
Exit criteria:
201232

@@ -298,7 +329,7 @@ Tasks:
298329
- [ ] Keep byte-level parser fuzzing in place
299330
- [ ] Keep hidden-Unicode rejection fuzzing in place
300331
- [ ] Keep typed-path fuzzing in place
301-
- [ ] Stabilize value-roundtrip fuzzing around documented guarantees
332+
- [-] Stabilize value-roundtrip fuzzing around documented guarantees
302333
- [ ] Add every real fuzz-found bug as a normal regression test
303334
- [ ] Add longer-running fuzz jobs outside the fast CI lane
304335

@@ -419,4 +450,5 @@ Entries:
419450
- `2026-03-31 | codex1 | WS1/WS3/WS4 | Documented the key/colon boundary: whitespace and newlines before ':' are canonicalized, comments there are rejected, and comments after ':' normalize to entry-prefix comments | this narrows the remaining ambiguity work to other still-undocumented map-key forms rather than generic separator trivia`
420451
- `2026-03-31 | codex1 | WS3 | Added formatter-core roundtrip coverage for literal keys plus multiline basic/literal string tokens in both key and value position | string-family coverage is broader, but full writer determinism across all nested shapes is still not declared complete`
421452
- `2026-03-31 | codex1 | WS1/WS8 | Documented and regression-tested the owned-Value exact-roundtrip exclusions already assumed by fuzzing: legacy unit variants collapse to strings, and core keyword map keys canonicalize to string keys | next contract work is to decide whether any additional fuzz exclusions should be promoted to documented non-guarantees`
453+
- `2026-03-31 | codex1 | WS1/WS8 | Added a short semantics draft to the tracker and tightened the documented exact-roundtrip exclusions: legacy and core keyword map keys canonicalize to strings, legacy unit variants collapse to strings, and fuzz-contract comments now point at tested behavior | next semantics work is to finish the remaining unsupported/tolerated ambiguity list rather than leave it implied`
422454
- `2026-03-31 | codex2 | WS7/WS9 | Added scripts/run_benchmark_baseline.sh and benchmark-data/README.md with a reproducible local baseline for bench_parse and bench_core_vs_serde at 477118a | next performance slice is release-size tracking or stronger benchmark automation`

crates/eon/tests/test_roundtrip_contract.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,28 @@ fn test_core_value_roundtrip_excludes_keyword_map_keys() {
2222
assert_ne!(parsed, original);
2323
}
2424

25+
#[test]
26+
fn test_legacy_value_roundtrip_excludes_keyword_map_keys() {
27+
let original = Value::Map(Map::from_iter([
28+
(Value::Null, Value::from(0)),
29+
(Value::Bool(true), Value::from(1)),
30+
(Value::Bool(false), Value::from(2)),
31+
]));
32+
33+
let serialized = original.format(&Default::default());
34+
assert_eq!(serialized, "null: 0\ntrue: 1\nfalse: 2\n");
35+
36+
let parsed: Value = serialized.parse().unwrap();
37+
let expected = Value::Map(Map::from_iter([
38+
(Value::from("null"), Value::from(0)),
39+
(Value::from("true"), Value::from(1)),
40+
(Value::from("false"), Value::from(2)),
41+
]));
42+
43+
assert_eq!(parsed, expected);
44+
assert_ne!(parsed, original);
45+
}
46+
2547
#[test]
2648
fn test_legacy_value_roundtrip_excludes_unit_variants() {
2749
let original = Value::Variant(Variant {

0 commit comments

Comments
 (0)