Agent-First Data v0.36.0: Edits That Reach, and Edits That Refuse
A document editor is trusted by two things: that it can reach every address it claims to, and that it says no when a write would do damage. This release closes gaps on both sides, and makes the four SDKs spell numbers and dates the same way.
An editor that rewrites a config file in place has two jobs that pull in opposite directions. It has to reach every address a caller can name, in every format it supports. And it has to refuse a write that would quietly destroy something. Most of this release is cases where one of those two failed.
Addresses that could not be written
YAML lists are where reach ran out. Three shapes had no way in:
- A list item that is a single-key mapping could be read but not extended with another field.
- A list element could be replaced by a scalar but not by a collection.
- An empty document had no mapping to put a first key into.
All three now work. An edit that goes through a YAML alias still stops, but it
now names the alias and says afdata does not edit through aliases, instead of
leaking the YAML parser’s own error. A final - on an array is the append
position in every verb, even when some element’s slug happens to be -; a -
in the middle of a path says it can only be the last segment.
Edits that kept what they should
TOML was the format where an edit lost things it had no business touching:
- A datetime written back stayed a datetime instead of turning into a string.
- Removing an array element kept the next element’s own-line comments and a
comment beside
[, and dropped the comments that belonged to the removed one. - A table created inside an inline table gets ordinary spacing, as in
{ b = 1, c = { d = 2 } }.
Unsetting an INI section now removes the section. A bad dotenv name blames the path that was given, not an internal one.
Writes that refuse
--secret-from reads a secret from somewhere and writes it into a field. The
overwrite guard that protects a mapping or a non-string value from being
replaced by a bare value ran after the secret had been read. It now runs
before, so a refused write never touches the secret source.
afdata value used to refuse any _url value whose redacted form differed from
the original, which meant mailto:x@y.z and https://h/a b failed with
document_secret_redacted. It now refuses only when a userinfo password or a
secret-named query or fragment parameter is actually present, found with a real
URL parser. A value the parser cannot read, or a schemeless user:pass@host, is
still refused.
A symlink guard that matched the final path segment by name failed on APFS when the link was stored decomposed and typed composed. It now matches among the parent’s entries by device and inode.
Skill administration failures carry their own codes: usage errors exit 2, and
the rest exit 1 as skill_refused, skill_io_failed or skill_spec_invalid.
An empty or relative HOME no longer installs a personal skill into the
current directory.
Four SDKs, one spelling
The same fixture suite that checks the four SDKs against each other found more places they differed:
- YAML and Plain output took each runtime’s own shortest-float spelling, and
those switch to exponent form at different magnitudes. Go wrote
1234567.5as1.2345675e+6;0.000015came out as1.5e-5in Go and Python but0.000015in Rust and TypeScript. The notation is now decided on the digits, positional in[1e-6, 1e21), for native floats and decoded literals alike. Digits as written are never changed. - Python read strings such as
١٢٣as integers, because its digit predicate accepts non-ASCII digits. Only ASCII digit strings are integers now. - TypeScript rounded fractional seconds differently from
printf; it now rounds the same way, and Python pads years below 1000 in RFC 3339 output on every platform. - JSON
-0survives a read in TypeScript. - The Go, Python and TypeScript emitters report
output_write_failedwhen output cannot be written, as Rust does.
The Go module is now tagged in its subdirectory, so go get resolves this
release.
v0.36.1
Every CLI rejection used to say “choose one registered combination”, so a caller
who passed one out-of-range value was sent off to compare argument sets. The hint
now follows the rule that was broken: a bad value, a missing value, a repeat, an
unknown command or argument, and invalid UTF-8 each name their own next step,
still with the --help command to run. afdata emit with an empty message or
code says what to pass.