feat(config): Introduce 'this_host' serialization logic and enforce consistent config formatting

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-05-22 15:10:46 -07:00
parent 42e71623a0
commit 8eea22aa40
2 changed files with 85 additions and 3 deletions

View file

@ -216,6 +216,10 @@ def _serialize(cfg: ClaireConfig) -> str:
lines: list[str] = [f'machine_id = "{cfg.machine_id}"']
if cfg.sync_secret is not None:
lines.append(f'sync_secret = "{cfg.sync_secret}"')
# Top-level scalars MUST come before any `[section]` header — once a
# table opens, subsequent KV pairs belong to it. Emit `this_host` here.
if cfg.this_host is not None:
lines.append(f'this_host = "{cfg.this_host}"')
lines.extend(
[
"",
@ -262,9 +266,6 @@ def _serialize(cfg: ClaireConfig) -> str:
f'"{h}" = {c}' for h, c in sorted(lim.per_host.items())
)
lines.append(f"per_host = {{ {inner} }}")
if cfg.this_host is not None:
lines.append("")
lines.append(f'this_host = "{cfg.this_host}"')
for h in cfg.known_hosts:
lines.append("")
lines.append("[[known_hosts]]")

View file

@ -124,6 +124,87 @@ def test_load_or_init_reads_per_host_caps(tmp_path: Path) -> None:
assert cfg.limits.cap_for("plum") == 4 # unnamed → default
# --- host detection / known_hosts ------------------------------------------
def test_this_host_label_explicit_overrides_hostname(tmp_path: Path) -> None:
cfg_path = tmp_path / "claire.toml"
cfg_path.write_text(
'machine_id = "m"\n'
'sync_secret = "s"\n'
'this_host = "plum"\n'
'\n[web]\nhost = "127.0.0.1"\nport = 8765\n',
encoding="utf-8",
)
cfg = load_or_init(cfg_path)
assert cfg.this_host == "plum"
assert cfg.this_host_label() == "plum"
def test_this_host_label_defaults_to_short_os_hostname(tmp_path: Path) -> None:
import socket
cfg = load_or_init(tmp_path / "claire.toml")
expected = socket.gethostname().split(".", 1)[0].lower()
assert cfg.this_host is None
assert cfg.this_host_label() == expected
def test_resolve_host_label_rewrites_local_to_this_host(tmp_path: Path) -> None:
cfg_path = tmp_path / "claire.toml"
cfg_path.write_text(
'machine_id = "m"\n'
'sync_secret = "s"\n'
'this_host = "plum"\n'
'\n[web]\nhost = "127.0.0.1"\nport = 8765\n',
encoding="utf-8",
)
cfg = load_or_init(cfg_path)
assert cfg.resolve_host_label("local") == "plum"
# Already-canonical labels pass through.
assert cfg.resolve_host_label("apricot") == "apricot"
def test_resolve_host_label_uses_aliases(tmp_path: Path) -> None:
cfg_path = tmp_path / "claire.toml"
cfg_path.write_text(
'machine_id = "m"\n'
'sync_secret = "s"\n'
'this_host = "plum"\n'
'\n[web]\nhost = "127.0.0.1"\nport = 8765\n'
'\n[[known_hosts]]\n'
'name = "apricot"\n'
'aliases = ["apri", "apr"]\n',
encoding="utf-8",
)
cfg = load_or_init(cfg_path)
assert cfg.resolve_host_label("apri") == "apricot"
assert cfg.resolve_host_label("apr") == "apricot"
assert cfg.resolve_host_label("apricot") == "apricot"
# An unrelated label is unchanged.
assert cfg.resolve_host_label("black") == "black"
def test_serialize_round_trips_host_detection(tmp_path: Path) -> None:
cfg_path = tmp_path / "claire.toml"
cfg_path.write_text(
'machine_id = "m"\n'
'this_host = "plum"\n'
'\n[web]\nhost = "127.0.0.1"\nport = 8765\n'
'\n[[known_hosts]]\n'
'name = "apricot"\n'
'aliases = ["apri"]\n'
'description = "dev box"\n',
encoding="utf-8",
)
load_or_init(cfg_path) # rewrites file (backfills sync_secret)
reloaded = load_or_init(cfg_path)
assert reloaded.this_host == "plum"
assert len(reloaded.known_hosts) == 1
assert reloaded.known_hosts[0].name == "apricot"
assert reloaded.known_hosts[0].aliases == ["apri"]
assert reloaded.known_hosts[0].description == "dev box"
def test_serialize_round_trips_per_host_caps(tmp_path: Path) -> None:
# A config missing sync_secret triggers the migration rewrite, which
# runs _serialize — the per_host map must survive the round-trip.