Agent-First Data v0.28.1: One Path, Many Nodes
Reading one field across a collection took three steps, and the middle one was sed against afdata's own output. A path may now contain `*`. Because `*` is a legal JSON key, the grammar gains `\*` to keep such documents addressable — which makes this a breaking change to path spelling.
The previous release added values, which reads many paths from a single parse
of a document. It fixed the cost of asking which element has name X — 632ms to
17ms across a 110-package lockfile — but not the awkwardness. This is what the
release pipeline’s own version checker looked like afterwards:
keys="$(afdata paths Cargo.lock package --input-format toml | sed 's/$/.name/')"
afdata values Cargo.lock $keys --input-format toml
Enumerate the children, append the field to each address with sed, then read
them back. The middle step is string surgery on afdata’s own output — a tool’s
user reaching around the tool to build addresses by hand. That code was written
an hour after values shipped, by someone trying to use it idiomatically, which
is about as clear a signal as the design is going to give.
A path can now say it directly:
$ afdata values Cargo.lock 'package.*.name' --input-format toml
agent-first-data
aho-corasick
android_system_properties
...
One call. The sed is gone.
The part that took the care
* is a legal key in JSON. Before this release, paths printed it and value
read it, exactly like any other key. Reserving the bare form for patterns would
have made every document carrying one unaddressable.
That is precisely the mistake this crate spent the last two releases undoing.
The empty string is also a legal key, and rejecting it did not make it
unreachable — only unspeakable, while paths went on emitting addresses that
value refused. Repeating that with * would have been worse for knowing
better.
So the grammar gains a third escape. \. embeds a dot, \\ embeds a backslash,
and now \* embeds a star; join_path escapes stars on the way out. A document
with a * key still round trips — paths just spells it \*, and that
spelling reads back. Both can appear in the same path: *.\* expands the outer
level and addresses a literal star at the inner one.
The rule that keeps it coherent is one spelling, one meaning. A bare * is
refused by parse_path outright rather than being read as a key in value
while expanding in values — a single spelling that means two things depending
on which command you typed is how a grammar stops being a grammar.
Getting that wrong is easy, and the first attempt did: after parsing, \* and
* are the same string, so the check that reserved the bare form rejected the
escaped one too — re-stranding the very key it existed to protect. The parse now
tracks which spelling was written.
Breaking, and worth saying plainly
This is a patch release that changes path spelling:
pathsprints\*where it printed*- a bare
*in a path is now an error instead of a key reference
No document in this repository has a * key, so the measured impact across
seven consumer tools is zero. But if you have one, escape it.
A patch number does not usually carry a change like this, and anyone pinning a caret range will receive it automatically. It is called out here rather than left to be discovered.