Skip to content

Commit 8f66aa2

Browse files
fix: reject orphan positional inline bodies in generic decoder (SPEC 16.5)
The object-body parser silently skipped any line it did not recognize as a section, a key=value field, or an inline array. A stray positional inline body (a pipe-delimited line with no eligible ^{} cell, e.g. a 2nd 'Bob|b@t.com' after a row's one inline cell was filled) fell through once the row count was satisfied and was DROPPED with no error - silent data loss, a lossless round-trip hole. Such lines are now rejected: a pipe-delimited line as orphan_inline_attachment, any other unrecognized line as invalid_line. Verified that no legitimate encoder output reaches this catch-all (full conformance + property round-trip + fuzz green; legitimate root fields after a nested ## section still decode).
1 parent 349c6db commit 8f66aa2

2 files changed

Lines changed: 9 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Fixes
66

77
- Decoder: reject an orphan `.field` attachment (a `.field` whose name is neither a `^`-marked column of its row nor a `>`-containing field name, SPEC 7.4.6.1.4) instead of silently absorbing it as an undeclared extra field. Such a stray attachment previously decoded to a record no encoder produces, silently injecting a field onto the last-parsed row (a lossless round-trip hole); now rejected per SPEC 16.5 (`orphan_attachment`).
8+
- Decoder: reject an orphan positional inline body (a pipe-delimited line with no eligible `^{}` attachment-marker cell) instead of silently dropping it. The object-body parser previously skipped any unrecognized line, so a stray positional body (e.g. a second `Bob|b@t.com` after a row's one inline cell was filled) vanished with no error (silent data loss); now rejected per SPEC 16.5 (`orphan_inline_attachment`).
89
- Graph streaming trailer: the edge count is now always the last `counts` entry, even when the stream has no edges (positional `counts=2,1,0`; labeled `counts=…,edges:0`). A zero-edge stream previously dropped it, violating the SPEC §8.4 / §8.4.1 rule that the edge count is always present and last (the invariant that keeps the positional form unambiguous). The graph trailer is decoder-ignored, so this changes producer output only.
910

1011
### Streaming: opt-in labeled trailer counts (SPEC §8.4.1)

src/gcf/decode_generic.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,14 @@ def _parse_object_body(
177177
i += 1
178178
continue
179179

180-
i += 1
180+
# An object-body line that is not a `## ` section, a `key=value` field, or
181+
# an inline array is not valid content and MUST NOT be silently skipped
182+
# (that dropped data, a lossless round-trip hole). A pipe-delimited line is
183+
# a stray positional inline body with no eligible `^` cell (SPEC 16.5,
184+
# orphan_inline_attachment); any other unrecognized line is likewise rejected.
185+
if "|" in content:
186+
raise ValueError(f"orphan_inline_attachment: {content}")
187+
raise ValueError(f"invalid_line: unexpected content in object body: {content!r}")
181188
return i - start
182189

183190

0 commit comments

Comments
 (0)