Context
luria upgrade yaml is the only way across the boundary ADR-098 drew: the
new version does not read TOML at all, so a record that has not crossed
cannot be linted, indexed or repaired. Every project on luria runs it exactly
once, and what it does is what that project’s config becomes.
It parsed with tomllib and wrote with a YAML emitter, for a good reason its
own docstring gives: a regex in a uid does not survive being moved as
bytes, because the two formats escape differently. Every value has to be
re-encoded by a writer that knows its own rules.
Comments are not values, and nothing carried them. tomllib returns a dict;
the comments are gone before any writer is chosen. The crossing printed what
it folded and what nothing read any more, and said nothing at all about the
prose it had just dropped.
The amounts are not incidental. This record’s own config lost 85 comment
lines when it crossed. anthology-of-the-sota, which had not crossed yet,
carried 273 — why each reference field is declared rather than merely
required, what promote_when is for, why contested_by is held to the same
standard as source:, what each of the two chains is. That is the reasoning
a config needs most, because a config’s values say what and almost never why.
Nor is the loss confined to the TOML. Each scheme’s vocabulary file was read
with yaml.safe_load before being inlined into vocabularies:, so the prose
above the thirteen topics went the same way.
Decision
The crossing carries comments.
A comment block is a run of comment lines plus the path of the first key or
table that follows it — that is all toml_comments.blocks recovers, and all
it needs to. The values still go through tomllib; the prose is attached
afterwards, to the key it was written above, through the ruamel API
yaml_edit already wraps.
Three details are load-bearing:
The document is round-tripped once before the comments go on. The values
come back from tomllib as plain dicts, and a plain dict has nowhere to hold
a comment. Dumping and reloading is what turns them into structures ruamel
can annotate.
A dotted key is a path. uris.title = "..." nests exactly as
[remotes.ARXIV.uris] plus title would. Read as a single key named
uris.title, its comment lands nowhere — which is how the real config’s one
stranded block was found, and it was the only one.
Prose that cannot be placed is printed in full, not counted. tags,
statuses and tag_groups do not exist on the far side: each splits into a
vocabulary named centrally and a field that names it. Comments on those
follow the field where the mapping is unambiguous; anything left is printed
whole, because a count tells you something was lost without telling you what,
which is the failure this whole change exists to stop (DP-1).
Alternatives considered
- Leave them in git history. The TOML is not deleted by the upgrade, and
git showcan recover any of it. But a config is read in the editor that has it open, and reasoning that requires knowing to look for a deleted file is reasoning nobody reads. The comments were written next to the keys because that is where they work. - Move the prose into
blurb:fields. This is what luria’s own config did after it crossed, and for vocabulary entries it is the right answer — a blurb is structured, and it renders into the docs. It does not generalize: there is no field onreferences.contested_bywhose value is “why this is held to the same standard assource:”, and inventing one for every key that might want a paragraph is a schema for prose. - Copy the comment bytes along with the value bytes. Rejected for values for a reason that does not apply to comments — escaping — but the shape is tempting because it is one pass. It would mean a TOML-shaped emitter writing YAML, which is how the escaping bug gets in through the back door.
- Status quo. Every project on luria crosses this boundary exactly once, and each one silently loses everything it wrote about its own config. Two had already paid it when this was written.
Consequences
The anthology’s crossing now carries all 42 of its blocks; before the
dotted-key fix it carried 41 and reported the 42nd. Its luria.yaml has 375
comment lines against the TOML’s 273 — the difference is the vocabulary-file
prose, which now survives too.
A paragraph break inside a block needed one more thing: ruamel writes an
empty comment line as an empty line, which is not a comment, so the prose
below it detached from the key it documented. toml_comments.rejoin puts the
bare # back. Cosmetic in a ten-line config and not in a 490-line one.
convert_config returns a fourth value, which is a signature change to a
function two callers use, both in this repository.
What this obliges: the carry is best-effort by construction, and it says so. A record whose TOML puts comments somewhere this does not look — inside a multi-line array, say, where a comment documents an element rather than a place — still loses them. That is a narrower hole than “all of them”, and the report is what makes it visible rather than silent.