From 4275adc4a5cc0a112a2730d874d69f86aa0bee14 Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Thu, 11 Jun 2026 18:20:21 +0000 Subject: [PATCH] watchdog: phase_done ignores placeholder '## DONE' sections (skipped mailu) A Builder scaffolded 'STATUS-mailu.md' with a '## DONE / Not yet. Written here only when ...' placeholder section; phase_done's startswith('## DONE') matched it and auto-advanced past mailu without any of its work being done (no recipe PR, no claim, no review). Harden phase_done: a '## DONE' heading counts only when its first non-empty body line is not a placeholder/negation (Not yet / pending / TBD / when all / <...> etc). Verified against all shipped STATUS files (real DONEs still detected; mailu placeholder rejected). --- cc-ci-plan/launch.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/cc-ci-plan/launch.py b/cc-ci-plan/launch.py index 6695682..575f2ad 100644 --- a/cc-ci-plan/launch.py +++ b/cc-ci-plan/launch.py @@ -215,12 +215,27 @@ def resolve_state(repo_dir, basename): p = Path(repo_dir) / "machine-docs" / basename return p if p.exists() else Path(repo_dir) / basename +# A "## DONE" heading marks real completion ONLY when its body is a substantive completion +# statement. A Builder that scaffolds a "## DONE / Not yet ..." placeholder section must not +# trip the auto-advance — that false-completed and SKIPPED the mailu phase once (2026-06-11). +_DONE_PLACEHOLDER_RE = re.compile( + r"^\s*(not yet|not done|not complete|incomplete|pending\b|tbd\b|n/?a\b|" + r"written here only|only when|to be (written|filled)|when all|<.*>)", re.I) + def phase_done(status_basename): path = resolve_state(BUILDER_DIR, status_basename) try: - return any(line.startswith("## DONE") for line in path.open()) + lines = path.read_text().splitlines() except FileNotFoundError: return False + for i, line in enumerate(lines): + if not line.startswith("## DONE"): + continue + body = next((nxt for nxt in lines[i+1:] if nxt.strip()), "") # first non-empty body line + if _DONE_PLACEHOLDER_RE.match(body): + continue # placeholder section ("Not yet ...") — keep scanning, not real + return True + return False # ── kickoff prompt ────────────────────────────────────────────────────────────