Core: cancel moved runs, retry bookkeeping, progress cap, safer classification
- A run whose item leaves In Progress is killed, gets a [worker] cancel comment, frees its terminal slot and serialize group and keeps its thread id; if the kill fails the slot stays taken and the cancel is retried next tick. - Transient retries are the same logical round: they do not count toward max_rounds_per_thread and do not trigger a max-rounds rebuild. - New project setting max_progress_rounds (default 12): after that many consecutive [progress] rounds the item goes to Waiting (answerer) with a note. - retry/rebuild markers are consumed only after the run launched, so a failed start no longer leaves an In Progress item that is never picked again; a rebuild is no longer reported as a retry. - State is saved after every finished round and every launched run. - Failure classification: transient patterns only look at the tail, and an exit-0 failure is logic (never retried) unless the tail shows a transient error. - First-line tag: tolerate BOM and a leading code fence / rule line. - Start comment notes when a handoff summary was attached.
This commit is contained in:
@@ -13,6 +13,18 @@ class FirstLineTest(unittest.TestCase):
|
||||
self.assertEqual(classify_first_line("**[blocked]** no device"), "blocked")
|
||||
self.assertEqual(classify_first_line("[progress]"), "progress")
|
||||
|
||||
def test_edge_cases(self):
|
||||
self.assertEqual(classify_first_line("\ufeff[review] ok"), "review")
|
||||
self.assertEqual(classify_first_line("```\n[progress] still going\n```"), "progress")
|
||||
self.assertEqual(classify_first_line("```text\n[blocked] no device\n```"), "blocked")
|
||||
self.assertEqual(classify_first_line("---\n[question] which?"), "question")
|
||||
self.assertEqual(classify_first_line("[progress]: step 2 of 5, next: tests"), "progress")
|
||||
self.assertEqual(classify_first_line("> `[review]` merged"), "review")
|
||||
self.assertIsNone(classify_first_line("note first\n[review] later")) # tag must lead
|
||||
msg = html_to_text(text_to_html("[codex] 第 1 轮结束", pre="\n [review] ok\n"))
|
||||
self.assertEqual(msg, "[codex] 第 1 轮结束\n\n [review] ok") # <pre>-wrapped body keeps its lines
|
||||
self.assertEqual(classify_first_line(html_to_text("<pre>\n[progress] x</pre>")), "progress")
|
||||
|
||||
def test_untagged(self):
|
||||
self.assertIsNone(classify_first_line("done, see [review] below"))
|
||||
self.assertIsNone(classify_first_line("[done] finished"))
|
||||
@@ -56,6 +68,13 @@ class FailureTest(unittest.TestCase):
|
||||
self.assertEqual(self.kind("whatever", timed_out=True), ("timeout", False))
|
||||
self.assertEqual(self.kind("window lost focus", cfg={"env_patterns": ["lost focus"]}), ("env", False))
|
||||
|
||||
def test_logic_is_never_transient_and_prose_is_ignored(self):
|
||||
self.assertEqual(self.kind("HTTP 429 Too Many Requests", exit_code=0), ("crash", True))
|
||||
r = ExecResult("tests timed out", 1, None, [], "")
|
||||
self.assertEqual(classify_failure(r, False, {}), ("crash", False))
|
||||
r = ExecResult("foreground window lost", 1, None, [], "")
|
||||
self.assertEqual(classify_failure(r, False, {"env_patterns": ["window lost"]}), ("env", False))
|
||||
|
||||
def test_custom_transient_patterns_replace_defaults(self):
|
||||
self.assertEqual(self.kind("timed out", cfg={"transient_patterns": ["flaky"]}), ("crash", False))
|
||||
self.assertEqual(self.kind("flaky relay", cfg={"transient_patterns": ["flaky"]}), ("crash", True))
|
||||
|
||||
Reference in New Issue
Block a user