Do not record a no-op consistency sync as a fix.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 1m11s

A binding put that only refreshes the sync timestamp, a push that reports the remote is already current, and a C-11 skip on a finished workplan are not repairs. Leave those out of FIXES APPLIED so a clean second run stays clean.

Assistant: grok
Assistant-Session: 01a16071-5425-78d3-80b4-20fe2ee30f0a
This commit is contained in:
tegwick 2026-10-22 09:28:10 +02:00
parent 64d698e65d
commit bd6fd62b0a
5 changed files with 130 additions and 10 deletions

View file

@ -2493,6 +2493,37 @@ def check_repo(
return report
def _bindings_already_synced(index: Any, bindings: list[dict[str, Any]]) -> bool:
"""True when the hub index already stores each binding's file location."""
if not isinstance(index, dict) or "_error" in index:
return False
workplans = index.get("workplans")
if not isinstance(workplans, dict):
return False
for binding in bindings:
current = workplans.get(binding["workplan_id"])
if not isinstance(current, dict):
return False
if current.get("filename") != binding["filename"]:
return False
if current.get("relative_path") != binding["relative_path"]:
return False
if bool(current.get("archived")) != bool(binding["archived"]):
return False
return True
def _record_push_seal(report: ConsistencyReport, ok: bool, msg: str) -> None:
"""Record a push that moved commits. An already-current remote is not a fix."""
if not ok:
report.fixes_applied.append(f"push WARN: {msg}")
return
lowered = msg.lower()
if "up-to-date" in lowered or "up to date" in lowered:
return
report.fixes_applied.append(f"push: {msg}")
def _sync_workplan_bindings(
api_base: str,
repo_slug: str,
@ -2540,10 +2571,16 @@ def _sync_workplan_bindings(
bindings.append(binding)
if not bindings:
return
# A timestamp-only binding write is not drift. Skip it when the index
# already has the same filename, path, and archive flag. A failed lookup
# still falls through to the put.
current = _api_get(api_base, "/workplans/index", {"refresh": "true"})
if _bindings_already_synced(current, bindings):
return
result = _api_put(api_base, "/workplans/index/bindings", {"bindings": bindings})
if isinstance(result, dict) and "_error" in result:
report.fixes_applied.append(f"bindings WARN: {result['_error']}")
elif isinstance(result, dict):
elif isinstance(result, dict) and int(result.get("updated") or 0) > 0:
report.fixes_applied.append(
f"bindings: synced {result.get('updated', 0)}/{result.get('received', len(bindings))}"
)
@ -4172,9 +4209,9 @@ def fix_repo(
# Skip creating tasks for finished workstreams — the workstream is
# done/archived so unlinked tasks are stale file artefacts, not gaps.
if normalise_workstream_status(ws_status) in CLOSED_WORKSTREAM_STATUSES:
report.fixes_applied.append(
f"C-11 skipped: task '{t_id}' in {ws_status} workstream — not created"
)
# The warn already names the unlinked task. Leaving it
# uncreated is not a repair, so it is not a fix.
pass
else:
t_status = normalise_task_status(task.get("status", "todo"))
t_priority = str(task.get("priority", "medium")).strip()
@ -4358,10 +4395,7 @@ def fix_repo(
# timer idempotent and preventing commit pile-up.
if repo_path:
push_ok, push_msg = _git_push(repo_path)
if push_ok:
report.fixes_applied.append(f"push: {push_msg}")
else:
report.fixes_applied.append(f"push WARN: {push_msg}")
_record_push_seal(report, push_ok, push_msg)
return report

View file

@ -74,7 +74,12 @@ def push_ff(repo_path: str) -> tuple[bool, str]:
capture_output=True, text=True, timeout=PUSH_TIMEOUT_SECONDS,
)
if result.returncode == 0:
return True, result.stdout.strip() or "pushed"
detail = "\n".join(
part.strip() for part in (result.stdout, result.stderr) if part and part.strip()
)
if "everything up-to-date" in detail.lower():
return True, "Everything up-to-date"
return True, detail or "pushed"
return False, result.stderr.strip() or "push failed"
except subprocess.TimeoutExpired:
return False, "push timed out; remote outcome unverified; inspect the remote ref before retrying"