Corruption reads fine but fails to parse; a plain read error says
nothing about the content, so it aborts the publish instead of
replacing an index whose versions other builds rely on.
The in-memory merge is written with the atomic write_file helper, so a
failed write keeps the live index by construction; the bool return, the
.merged sibling and its cleanup all go away, and a missing or unreadable
live index collapses into one branch. A shared error tuple also stops a
non-dict index entry from escaping the best effort handler, and the
reconfigure failure test now stubs the hint printer instead of resolving
a real IDF install.
A first sync stays silent, an unreadable live index is replaced with a
warning to heal it, and a failed merge write skips promoting that index
so its listed versions survive; the synced version then shows uncovered
and a later run retries.
The YAML shorthand keeps its == operator, so those exact pins now join
the manifest pass; coverage requires the checksums file the index names,
since the manager downloads it with no registry fallback; sync output is
decoded as UTF-8 with replacement so a stray byte cannot escape the best
effort handler; a skipped index merge is logged.
A merged-index write that fails partway can no longer promote a
truncated file; the shared rmtree helper replaces the silent
ignore_errors delete, so stale staged files abort the sync instead of
being promoted.
A top level yaml import taxed every upload subprocess; it now loads
inside the one function that parses YAML, and the lazy import test
watches for it. The mirror env moves next to its sync gate behind one
_esphome_manages_idf predicate, so the two sides cannot diverge.
Promote renames go through the shared retry helper for Windows sharing
violations, ServiceDep prints its own spec, and the module docstring
records the registry sync fan out tradeoff. Duplicate and stale tests
cleaned up.
A concurrent configure can no longer see an index entry whose archive
has not landed; a clean sync that still leaves a dependency uncovered
now warns and trips the retry guard instead of resyncing every run;
mixed case manifest keys are lowercased to match the registry's paths;
lock contention stays a quiet skip while any other lock error warns
and counts as a failed attempt.
The manager writes its files in place, so syncing into the live mirror
could expose a truncated index to a process configuring at the same
time; renames are atomic, and the staged index is merged with the
mirror's existing versions before promotion.
Both parsers now share one dependency iterator; the lock acquire has a
single except since filelock's Timeout is an OSError; the subprocess try
covers only the run itself; run_compile syncs only on the up-to-date
branch, since the reconfigure branches sync inside run_reconfigure; the
sync takes the lock and manifest paths explicitly so toolchain spells
them the same way has_outdated_files does.
The manifest is written before the first configure and carries exact pins,
so syncing from it lets a fresh solve install from the mirror instead of
downloading every archive twice. The sync moves into run_reconfigure so
every configure path is covered, and the hand-rolled lock file becomes the
filelock pattern the repo already uses, which the OS releases on a crash.