From 98fc68a3f3df398bff5eb0a88f9886721a72d217 Mon Sep 17 00:00:00 2001 From: Eugene Eisenstein Date: Thu, 3 Sep 2026 17:40:11 -0400 Subject: [PATCH] fix(sandbox): stop the seed retrying non-idempotent writes `up` intermittently failed with "expected 6 messages, found 12". Creating messages is not idempotent, and the SDK retries on 429/500/502/503/504 as well as on timeouts, so a write that commits and then errors on the way back is replayed and the fixture is seeded twice. The window is the cold api that cmd_seed has just restarted to apply migrations. The seed now runs with max_retries=0. The sandbox is local and always seeds from a cleared database, so a transient failure should stop and be re-run rather than be papered over into a silently doubled fixture -- and the doubling is not silent by luck alone: the message-count assertion is what caught it. Verified: three consecutive down/up cycles from clean volumes, 6 posted and 6 verified each time. --- sandbox/seed.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sandbox/seed.py b/sandbox/seed.py index 59d19e14..6de4deb0 100644 --- a/sandbox/seed.py +++ b/sandbox/seed.py @@ -146,7 +146,21 @@ def main() -> int: base_url = os.environ.get("SANDBOX_BASE_URL", "http://127.0.0.1:18000") workspace_id = fixture["workspace"] - honcho = Honcho(base_url=base_url, workspace_id=workspace_id, api_key="sandbox") + honcho = Honcho( + base_url=base_url, + workspace_id=workspace_id, + api_key="sandbox", + # No retries. Creating messages is not idempotent, and the SDK retries on + # 429/500/502/503/504 as well as timeouts, so a write that commits and then + # errors on the way back gets replayed -- seeding the fixture twice. That + # was observed as "expected 6 messages, found 12" on a cold api, right + # after cmd_seed restarts it. + # + # The sandbox is local and `up` always seeds from a cleared database, so a + # transient failure here should stop and be re-run, not be papered over + # into a silently doubled fixture. + max_retries=0, + ) peers = {spec["id"]: honcho.peer(spec["id"]) for spec in fixture["peers"]} session = honcho.session(fixture["session"])