Skip to content

levain 0.5.8

Choose a tag to compare

@phillipclapham phillipclapham released this 04 Oct 15:40
· 4395 commits to main since this release

levain wrap stops claiming things the store does not show: after a stop, a failed save, or a failure while showing the result, its messages now say only what was established (and name the command that settles the rest), --reset clears a wrap whose episode list anneal cannot read, and the symlink-loop warning fires on Python 3.13 as on 3.12.

Fixed

  • levain wrap --reset could not clear a wrap whose episode list anneal could not read (new in 0.5.7). Every wrap levain wrap opens is token-bound, so when the snapshot is unreadable --reset and the unattended discard now name the store's bound token and clear the wrap by that compare-and-swap, which still leaves any wrap that replaced it alone. (A wrap another program opened with a caller-supplied token is bound too, and is cleared the same way when it passes the same lock and age check.) A wrap with no token to name is refused as before, with the commands for that entity's store.
  • The wall-clock stop report claimed things the store did not show (since 0.4.x). It said the wrap was "CANCELLED cleanly" even when the cancel had failed (the store locked by another process, so the wrap stays open), and also after a stop that landed when no wrap of that run was open. It now follows what the cancel found: cancelled (the clean wording), failed (the wrap may still be open, and what clears it), or no open wrap of this run (neither claim, pointing at the dry run and the continuity file).
  • After a failed save with nothing left in progress, levain wrap said the memory COMMITTED and not to re-run, even when another program had cleared the wrap and nothing was saved (since 0.4.x). It now claims COMMITTED only with positive evidence (the last completed wrap id moved and anneal's own "preserved at" recovery text is in the error), NOT saved when its own wrap is still in progress (a refused compose) or when the id did not move and the episodes are still unconsolidated, and otherwise (including when the store cannot be read) says the store does not show whether this memory was recorded and to check with levain wrap --dry-run.
  • A seat's second-phase consolidate that exited non-zero was reported as leaving the memory UNCHANGED, including exit 1 after a save that had committed (since 0.4.x). The line now points at the consolidate's own diagnostic for what was established about the memory.
  • An error raised while showing the result after the memory was saved was reported as "could not read the store" and exit 2 (since 0.4.x). It now says the memory was saved, that there is nothing to re-run, and exits 0.
  • On Python 3.13 a symlink loop at .levain/memory.continuity.md booted the entity seed-only without the warning Python 3.12 gives. Path.resolve() stopped raising on a loop in 3.13, so the guard's warning never fired. The warning now fires on every version; the boot is the same seed-only boot.

Changed

  • The symlink-loop test is no longer skipped on Python 3.13, where CI already ran the suite.

Known open issues

  • A peer that completes Levain's wrap and immediately opens its own, between the compose and the save, can make a failed save report "NOT saved" (since 0.4.x). Both programs would have to act inside the same few milliseconds; the message names the wrap as refused when its memory was in fact recorded by the peer, and levain wrap --dry-run settles it.
  • A stop that lands before the main cleanup begins can leave the store open and the wrap lock held in that process (since 0.4.x): while the store is being opened or the wrap lock is taken. A second stop landing while levain wrap is closing the store can skip releasing the lock the same way. A command-line run exits, so this matters only to code that runs levain wrap inside a long-lived process.
  • The hard-stop report (the backstop that terminates the process outright) always says nothing was written and that the wrap is left in progress (since 0.4.x). It cannot inspect the store from there, so it does not know whether a wrap was open or whether the save had committed; levain wrap --dry-run settles it.
  • A second stop that lands while levain wrap is cancelling can leave the wrap open (since 0.4.x). A Ctrl-C followed by the wall-clock stop while the cancel is running strands the wrap until the next run's discard or --reset.
  • --reset and the unattended discard cannot tell a dead wrap from another program's live one beyond Levain's lock and the wrap's age (since 0.4.x). They clear the wrap they read by its token; whether that wrap is dead is the operator's judgement for --reset.
  • On a Linux host with a rootful container daemon whose directory only root can read (for example /run/podman), the floor refuses to give the entity bash. That is the hardlink check refusing a crown jewel it cannot stat, by design; making the directory readable to the user is the stated remedy. Whether such a host should refuse by default is undecided.