feat(registry): format version 2, migrated by the writer (Issue #71)

- lib_py/registry_format.py: a pure, idempotent migrate() drops
  kill_command/start_command and rebuilds attach_command on every row
  (stopped/terminated included) and sets the top-level registry_format key.
- atomic_dump_yaml migrates an older registry under its lock, before the
  caller's mutation, and then rewrites the YAML as well as the DB (the YAML
  write is otherwise skipped when only fields change). load_state_json stays
  read-only. New registries are written with the current version.
- Entry point for #72: `python -m lib_py.registry_format --yaml <path>` runs
  the migration through the writer and prints
  `registry-format: <from> -> <to>, rows changed: <n>`.

Refs #71

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-10-25 15:16:46 +00:00
1 parent 50e2998a20
commit 9c21fa4e4f
3 files changed
+117 -1

No files matched your search

+13 -1
View File
@@ -4,6 +4,7 @@
import os, sys, tempfile, shutil, glob, subprocess, json, sqlite3, fcntl
from datetime import datetime, timezone
import yaml
from lib_py import registry_format
def atomic_dump_yaml_main():
yaml_path = os.environ['YAML_PATH']
@@ -114,6 +115,16 @@ def atomic_dump_yaml_main():
elif 'herdr_sessions' not in d:
d['herdr_sessions'] = []
# Issue #71: an older registry is migrated here, under the lock and
# before the caller's mutation. It changes row fields only, so the
# YAML is rewritten whether or not the mutation changes anything.
from_version = registry_format.stored_version(d)
migrated_rows = 0
force_yaml = False
if from_version < registry_format.CURRENT:
d, migrated_rows = registry_format.migrate(d)
force_yaml = True
old_sessions = get_all_sessions_status(d)
# a mutation that replaces a row the monitor registered for a pane it
# created itself (create_session.sh) drops that name from old_roles
@@ -167,7 +178,7 @@ def atomic_dump_yaml_main():
conn.commit()
if new_sessions != old_sessions:
if force_yaml or new_sessions != old_sessions:
if os.path.exists(yaml_path):
try:
shutil.copy2(yaml_path, yaml_path + '.bak')
@@ -207,6 +218,7 @@ def atomic_dump_yaml_main():
_flock_f.close()
except Exception:
pass
return from_version, registry_format.stored_version(d), migrated_rows
if __name__ == '__main__':
atomic_dump_yaml_main()