Skip to content

Commit af1de0c

Browse files
committed
brain: board publish idempotence ignores the timestamp
The unchanged-observation check compared exact file text, but the payload embeds ts — so a re-publish crossing a second boundary pushed a no-op commit and re-triggered the board (caught by the 3.13 CI leg racing the clock; 3.12 ran inside one second). Compare with ts stripped: an unchanged observation never re-pushes. Test pins ts-insensitivity explicitly. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d533fa0 commit af1de0c

2 files changed

Lines changed: 24 additions & 4 deletions

File tree

board/_publish.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,20 @@ def main(argv=None):
160160
return 2
161161

162162
DEVBOX_FILE.parent.mkdir(parents=True, exist_ok=True)
163-
if DEVBOX_FILE.exists() and DEVBOX_FILE.read_text() == text:
164-
print("board publish: devbox observation already current — nothing to push")
165-
return 0
163+
# Idempotence ignores the timestamp: an unchanged observation must not
164+
# bump the file (and re-trigger brain_board.yml) just because the clock
165+
# moved between two runs.
166+
if DEVBOX_FILE.exists():
167+
try:
168+
prev = json.loads(DEVBOX_FILE.read_text())
169+
except (json.JSONDecodeError, OSError):
170+
prev = None
171+
if prev is not None and \
172+
{k: v for k, v in prev.items() if k != "ts"} == \
173+
{k: v for k, v in payload.items() if k != "ts"}:
174+
print("board publish: devbox observation already current — "
175+
"nothing to push")
176+
return 0
166177
DEVBOX_FILE.write_text(text)
167178

168179
rel = os.path.relpath(DEVBOX_FILE, PUBLISH_REPO)

tests/test_board.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,19 @@ def test_publish_commits_and_pushes_to_main_only(tmp_path):
519519
assert shown.returncode == 0
520520
assert json.loads(shown.stdout)["worktrees"][0]["repo"] == "RepoA"
521521
assert "hygiene" not in json.loads(shown.stdout) # --no-hygiene
522-
# Re-publishing an identical observation pushes nothing new.
522+
# Re-publishing an identical observation pushes nothing new — and the
523+
# comparison ignores the timestamp by design (an unchanged observation
524+
# must not re-trigger the board just because the clock moved).
523525
r2 = subprocess.run([str(BRAIN), "board", "publish", "--no-hygiene"],
524526
capture_output=True, text=True, env=env, cwd=tmp_path)
525527
assert "nothing to push" in r2.stdout
528+
state_file = brain / "state" / "devbox_board.json"
529+
stored = json.loads(state_file.read_text())
530+
stored["ts"] = "2020-01-01T00:00:00Z"
531+
state_file.write_text(json.dumps(stored, indent=2, sort_keys=True) + "\n")
532+
r2b = subprocess.run([str(BRAIN), "board", "publish", "--no-hygiene"],
533+
capture_output=True, text=True, env=env, cwd=tmp_path)
534+
assert "nothing to push" in r2b.stdout
526535
# Off main, publish refuses (guard against feature-branch commits).
527536
subprocess.run(["git", "-C", str(brain), "checkout", "-qb", "other"],
528537
check=True)

0 commit comments

Comments
 (0)