turncappark.py 4.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. """The turn-cap checkpoint-park bundle (goal 28 task D2).
  2. The daemon's FIRST turn-cap hit pushes `suspended` with reason
  3. `turn_cap`; the web parks the task with the one-shot wrap-up notice
  4. (immediately resume-offerable — no wake source needed) and stamps
  5. `turn_cap_parked` so the resuming assignment carries the flag. The
  6. next cap hit arrives as the ordinary failed runaway push and ends the
  7. task for good.
  8. """
  9. from harness import * # noqa: F401,F403 — the shared wire helpers
  10. def run(ctx):
  11. auth, fake, fake_id, fake_token = ctx.auth, ctx.fake, ctx.fake_id, ctx.fake_token
  12. print("== goal 28 D2: the turn-cap checkpoint-park ==")
  13. fake.ask_on_register = False
  14. fake.hold_state = True
  15. task_id = create_queued(auth, {
  16. "project_id": "1", "title": "Runaway: parks at the first turn cap",
  17. "priority": "2"})
  18. ask_status, assignment = fake.ask_now()
  19. check(ask_status == 200 and int(assignment["task"]["id"]) == task_id,
  20. "the ask claims the queued task")
  21. fake.state = "working"
  22. check(fake.push_results("working") == 200, "the working push opens the round")
  23. # The first cap hit: a suspended transition carrying reason turn_cap
  24. # (the summary names the cap the way the daemon words it).
  25. fake.state = "suspended"
  26. check(fake.push_results("suspended",
  27. summary="turn cap park: 400 assistant turns without "
  28. "reaching a terminal state (cap 400) — parked "
  29. "for wrap-up, not failed",
  30. reason="turn_cap") == 200,
  31. "the turn-cap park transition lands")
  32. conn = db()
  33. row = conn.execute(
  34. "select status, pending_resume_notice, turn_cap_parked from tasks where id = ?",
  35. (task_id,)).fetchone()
  36. check(row is not None and row[0] == "suspended",
  37. "the task row parks suspended (not failed)")
  38. check(row is not None and row[1] is not None and "WRAP UP NOW" in row[1]
  39. and "stopped for good" in row[1],
  40. "the one-shot wrap-up notice is set on the row")
  41. check(row is not None and row[2] in (1, True),
  42. "the lineage flag turn_cap_parked is stamped")
  43. # The park is IMMEDIATELY resume-offerable (the notice is the lift):
  44. # the very next ask hands the SAME task back with the wrap-up as its
  45. # resume_notice and the turn_cap_parked flag riding the assignment.
  46. resume_status, resumed = fake.ask_now()
  47. check(resume_status == 200 and int(resumed["task"]["id"]) == task_id,
  48. "the parked task is immediately offerable again")
  49. check("Turn cap park" in (resumed.get("resume_notice") or ""),
  50. "the wrap-up notice rides the resuming assignment")
  51. check(resumed.get("turn_cap_parked") is True,
  52. "the assignment carries turn_cap_parked")
  53. # The notice is one-shot: consumed by the allocating assignment.
  54. conn = db()
  55. row = conn.execute(
  56. "select pending_resume_notice from tasks where id = ?", (task_id,)).fetchone()
  57. check(row is not None and row[0] is None,
  58. "the one-shot notice is consumed by the allocation")
  59. # The lineage flag SURVIVES the resume (a second cap hit must still
  60. # be final), and rides the fresh working round's next assignment.
  61. conn = db()
  62. row = conn.execute(
  63. "select turn_cap_parked from tasks where id = ?", (task_id,)).fetchone()
  64. check(row is not None and row[0] in (1, True),
  65. "the lineage flag survives the resume")
  66. # The resumed run reaches the cap again: the ordinary failed runaway
  67. # push — with turn_cap_parked on the row, this is the final kill.
  68. fake.state = "working"
  69. check(fake.push_results("working") == 200, "the resumed round opens")
  70. fake.state = "failed"
  71. check(fake.push_results("failed",
  72. summary="runaway session: a resumed run reached the turn "
  73. "cap again — 400 assistant turns without a terminal "
  74. "state, cap 400 (SADDLE_TURN_CAP overrides it), "
  75. "aborting like the degeneration path") == 200,
  76. "the second cap hit's failed push lands")
  77. wait_for(lambda: task_state(auth, task_id).get("status") == "failed"
  78. or False, timeout=30, what="the runaway task failed for good")