Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 50 additions & 12 deletions skills/codex-refactor-loop/scripts/phase9_router_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,51 @@
*LIFECYCLE_PREFIXES,
)
MARKER_RE = re.compile(r"\b(?:[A-Z][A-Z0-9_]*_(?:DONE|RESOLVED|BLOCKED)|META_JUDGE_DONE):[^\s`]+")
# Refactor (iter4/skill-router-fallback-flood-fix): Old pattern: accepted any
# marker payload, treating regex fragments containing `|` / `\"` / `*` / `r+1`
# / backslashes as real markers, flooding pending-events for every old log.
# New principle: real marker prefix and suffix segments contain only
# [A-Za-z0-9_./\-] plus limited punctuation; reject other special characters as
# prompt/regex echoes (per 2026-05-26 maintainer-directive).
VALID_MARKER_PAYLOAD = re.compile(r"^[A-Z][A-Z0-9_]*:[A-Za-z0-9_./\-]+(?::[A-Za-z0-9_./\-]+)*$")


class Phase9MarkerGrammar:
# Refactor (iter1/issue-149): refactor helper, no behavior change outside existing routes.
# Old pattern: phase9_router_daemon marker parser 不能可靠识别含中文收敛问题/route 后缀的 judge marker → 漏派 triplet judge 与 converge round,controller 被迫 fallback 全部 dispatch(本会话持续 no-gap churn 根因)。
# New principle: 按 .refactor-loop/runs/phase9-issue149-r2-judge.md consensus(structural):route-specific marker-grammar parser fix,正确解析所有 route marker(含中文 body),不引入 Phase9RoundProjection 抽象。使 router 对所有 glob 可见的 3/3 SOLVER_DONE triplet 与 converge 可靠 dispatch。硬约束:不重建 REFERENCE.md;refactor 注释自含 Old/New;不超范围。
ROUTE_TOKEN = re.compile(r"^[A-Za-z0-9_./-]+$")
VERDICT_TOKEN = re.compile(r"^[A-Za-z0-9_./-]+$")
CONVERGE_RE = re.compile(r"^META_JUDGE_DONE:converge:round-(\d+)(?::.*)?$")

@classmethod
def parse_marker_candidate(cls, text: str) -> str | None:
if cls.is_solver_done(text) or cls.parse_converge_round(text) is not None or cls.is_stalled_marker(text):
return text
if cls._is_lifecycle_or_unknown_marker(text):
return text
return None

@classmethod
def is_solver_done(cls, marker: str) -> bool:
parts = marker.split(":", 3)
return (
len(parts) >= 3
and parts[0] == "SOLVER_DONE"
and parts[1] in ROLES
and bool(cls.VERDICT_TOKEN.match(parts[2]))
)

@classmethod
def parse_converge_round(cls, marker: str) -> int | None:
match = cls.CONVERGE_RE.match(marker)
return int(match.group(1)) if match else None

@classmethod
def is_stalled_marker(cls, marker: str) -> bool:
return marker.startswith("META_JUDGE_DONE:escalate:stalled:")

@classmethod
def _is_lifecycle_or_unknown_marker(cls, marker: str) -> bool:
parts = marker.split(":")
if len(parts) < 2:
return False
if not re.match(r"^[A-Z][A-Z0-9_]*(?:_DONE|_RESOLVED|_BLOCKED)?$", parts[0]):
return False
return all(cls.ROUTE_TOKEN.match(part) for part in parts[1:])


@dataclass(frozen=True)
Expand Down Expand Up @@ -212,6 +250,9 @@ def _collect_markers(self) -> list[Marker]:
return markers

def _extract_marker(self, line: str) -> str | None:
# Refactor (iter1/issue-149):
# Old pattern: phase9_router_daemon marker parser 不能可靠识别含中文收敛问题/route 后缀的 judge marker → 漏派 triplet judge 与 converge round,controller 被迫 fallback 全部 dispatch(本会话持续 no-gap churn 根因)。
# New principle: 按 .refactor-loop/runs/phase9-issue149-r2-judge.md consensus(structural):route-specific marker-grammar parser fix,正确解析所有 route marker(含中文 body),不引入 Phase9RoundProjection 抽象。使 router 对所有 glob 可见的 3/3 SOLVER_DONE triplet 与 converge 可靠 dispatch。硬约束:不重建 REFERENCE.md;refactor 注释自含 Old/New;不超范围。
stripped = line.strip().strip("`")
if self._is_placeholder_or_echo(stripped):
return None
Expand All @@ -228,9 +269,7 @@ def _extract_marker(self, line: str) -> str | None:
candidate = match.group(0).rstrip("`.,);:|\"\\")
if candidate is None:
return None
if not VALID_MARKER_PAYLOAD.match(candidate):
return None
return candidate
return Phase9MarkerGrammar.parse_marker_candidate(candidate)

def _is_placeholder_or_echo(self, text: str) -> bool:
if "<" in text and ">" in text:
Expand Down Expand Up @@ -395,8 +434,7 @@ def _directly_handled(self, marker: Marker, ledger: set[str]) -> bool:
return False

def _round_from_converge(self, marker: str) -> int | None:
match = re.match(r"META_JUDGE_DONE:converge:round-(\d+):", marker)
return int(match.group(1)) if match else None
return Phase9MarkerGrammar.parse_converge_round(marker)

def _stalled_predicate_holds(self, issue: str, round_no: int) -> bool:
if round_no < 3:
Expand Down
71 changes: 70 additions & 1 deletion skills/codex-refactor-loop/scripts/test_phase9_router_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,44 @@ def test_phase9_router_solver_triplet_dispatches_meta_judge_once(self) -> None:
self.assertIn(str((self.repo / ".refactor-loop" / "logs" / "phase9-issue37-r4-judge.log").resolve()), command)
self.assertEqual(self.ledger_entries()[0]["key"], "37-4-judge")

def test_phase9_router_solver_triplet_accepts_non_ascii_summary(self) -> None:
# Refactor (iter1/issue-149):
# Old pattern: phase9_router_daemon marker parser 不能可靠识别含中文收敛问题/route 后缀的 judge marker → 漏派 triplet judge 与 converge round,controller 被迫 fallback 全部 dispatch(本会话持续 no-gap churn 根因)。
# New principle: 按 .refactor-loop/runs/phase9-issue149-r2-judge.md consensus(structural):route-specific marker-grammar parser fix,正确解析所有 route marker(含中文 body),不引入 Phase9RoundProjection 抽象。使 router 对所有 glob 可见的 3/3 SOLVER_DONE triplet 与 converge 可靠 dispatch。硬约束:不重建 REFERENCE.md;refactor 注释自含 Old/New;不超范围。
for role in ("minimal", "structural", "delete"):
self.write_log(
f"phase9-issue149-r1-{role}.log",
f"SOLVER_DONE:{role}:propose:中文摘要-继续收敛",
)

self.router.tick()

self.assertEqual(len(self.commands), 1)
self.assertIn("phase9-issue149-r1-judge.log", " ".join(self.commands[0]))
self.assertEqual([entry["key"] for entry in self.ledger_entries()], ["149-1-judge"])

def test_phase9_router_controller_dispatched_triplet_across_ticks(self) -> None:
for role in ("minimal", "structural"):
self.write_log(
f"phase9-issue149-r2-{role}.log",
f"SOLVER_DONE:{role}:propose:中文摘要-等待第三路",
)

self.router.tick()
self.assertEqual(self.commands, [])
self.assertEqual(self.ledger_entries(), [])

self.write_log(
"phase9-issue149-r2-delete.log",
"SOLVER_DONE:delete:propose:中文摘要-第三路完成",
)
self.router.tick()
self.router.tick()

self.assertEqual(len(self.commands), 1)
self.assertIn("phase9-issue149-r2-judge.log", " ".join(self.commands[0]))
self.assertEqual([entry["key"] for entry in self.ledger_entries()], ["149-2-judge"])

def test_phase9_router_accepts_solver_issue_logs_for_triplet(self) -> None:
for role in ("minimal", "structural", "delete"):
self.write_log(
Expand Down Expand Up @@ -238,6 +276,24 @@ def test_phase9_router_converge_dispatches_next_round_solvers(self) -> None:
["37-5-delete", "37-5-minimal", "37-5-structural"],
)

def test_phase9_router_converge_accepts_non_ascii_reason(self) -> None:
self.write_log(
"phase9-issue149-r2-judge.log",
"META_JUDGE_DONE:converge:round-3:中文收敛问题-继续三路判断",
)

self.router.tick()

self.assertEqual(len(self.commands), 3)
logs = " ".join(" ".join(command) for command in self.commands)
self.assertIn("phase9-issue149-r3-minimal.log", logs)
self.assertIn("phase9-issue149-r3-structural.log", logs)
self.assertIn("phase9-issue149-r3-delete.log", logs)
self.assertEqual(
sorted(entry["key"] for entry in self.ledger_entries()),
["149-3-delete", "149-3-minimal", "149-3-structural"],
)

def test_phase9_router_accepts_meta_judge_issue_log_for_converge(self) -> None:
self.write_log("meta-judge-issue100-r2.log", "META_JUDGE_DONE:converge:round-3:need-more")

Expand Down Expand Up @@ -567,6 +623,7 @@ def test_phase9_router_persists_fallback_dedup_across_restart(self) -> None:

def test_phase9_router_rejects_junk_markers_with_regex_special_chars(self) -> None:
"""Markers containing pipe/quote/backslash/template chars are prompt/regex echoes, not real markers."""
self.write_log("phase9-issue41-r1-judge.log", "META_JUDGE_DONE:converge:round-2:中文收敛问题-合法")
junk_lines = [
'grep "META_JUDGE_DONE:converge:r+1" log',
'pattern META_JUDGE_DONE:converge:round-2:With|round-3|Choose|minimal\\""',
Expand All @@ -581,7 +638,8 @@ def test_phase9_router_rejects_junk_markers_with_regex_special_chars(self) -> No
self.router.tick()

events = self.pending_events()
self.assertEqual(self.commands, [])
self.assertEqual(len(self.commands), 3)
self.assertTrue(all("phase9-issue41-r2-" in " ".join(command) for command in self.commands))
for forbidden_token in ("r+1", "round-3|Choose", "no-op;", "stalled:*", "CANONICAL_HUMAN_LABELS"):
with self.subTest(forbidden_token=forbidden_token):
self.assertNotIn(
Expand Down Expand Up @@ -611,6 +669,17 @@ def test_phase9_router_source_does_not_introduce_forbidden_abstractions(self) ->
f"phase9_router_daemon.py must not introduce forbidden boundary token: {forbidden}",
)

def test_phase9_router_marker_grammar_is_route_specific_not_ascii_payload_gate(self) -> None:
src = PHASE9_ROUTER.read_text(encoding="utf-8")

self.assertIn("class Phase9MarkerGrammar", src)
self.assertIn("def parse_marker_candidate", src)
self.assertIn("def parse_converge_round", src)
self.assertIn("def is_stalled_marker", src)
self.assertNotIn("class Phase9RoundProjection", src)
self.assertNotIn("Phase9RoundProjection(", src)
self.assertNotIn("VALID_MARKER_PAYLOAD.match(candidate)", src)

def test_main_once_dispatches_via_temp_repo_root(self) -> None:
self.solver_triplet(issue=37, round_no=4)
commands: list[list[str]] = []
Expand Down
Loading