forcewake.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. """The forcewake bundle (goal 4 task 1): the human-triggered resume.
  2. Self-contained on purpose: the goals/allocation shards die
  3. environmentally mid-run in this container (the known framework stack
  4. corruption) long before their new sections, so the force-wake
  5. behaviour gets its own short-lived app here. The extended sections
  6. stay in their home bundles for when task 3 fixes the environment.
  7. """
  8. from harness import * # noqa: F401,F403 — the shared wire helpers
  9. def run(ctx):
  10. auth, fake, fake_id, fake_token = ctx.auth, ctx.fake, ctx.fake_id, ctx.fake_token
  11. def create_goal(fields):
  12. act(auth, slot_public(auth, "/goals", "goals")["create"], fields)
  13. rows = [g for g in slot_public(auth, "/goals", "goals")["goals"]
  14. if g["title"] == fields["title"]]
  15. return int(rows[0]["id"])
  16. def run_task_of(goal_id):
  17. conn = db()
  18. try:
  19. return int(conn.execute(
  20. "select id from tasks where goal_id = ? and kind = 'goal_run'",
  21. (goal_id,)).fetchone()[0])
  22. finally:
  23. conn.close()
  24. def run_status(goal_id):
  25. return task_state(auth, run_task_of(goal_id))["status"]
  26. print("== goal 4 t1: force wake — the goal-2 stall class ==")
  27. # Create → plan → approve → parked: the goalui-proven short flow.
  28. # (Goal 27 refinements — the parking guard: the park needs a live
  29. # wake source; the blocked worker is it. The never-enqueued DRAFT
  30. # below still produces no transition — the stall the wake recovers.)
  31. fake.goal_script = [("plan", "# Wake plan\n\n1. Park safely")]
  32. goal = create_goal({"project_id": "1", "title": "Force-wake goal",
  33. "brief": "Parks with a never-enqueued draft.",
  34. "branch_name": "goal/forcewake"})
  35. conn = db()
  36. conn.execute("insert into tasks (project_id, goal_id, kind, title, status,"
  37. " priority, mode, creator, created_at) values (1, ?, 'task',"
  38. " 'Wake live worker', 'blocked', 5, 'implement', 'goal',"
  39. " strftime('%s','now'))", (goal,))
  40. conn.commit()
  41. conn.close()
  42. act(auth, goal_state(goal)["start"], {})
  43. check(wait_for(lambda: goal_state(goal)["status"] == "plan_review" or False,
  44. timeout=90, what="goal plans") is not None,
  45. "the goal reaches plan review")
  46. fake.goal_script = [("suspend",)]
  47. pending = goal_state(goal)["plan_pending_block"]
  48. act(auth, pending["answer_action"], {"verdict": "accept"})
  49. check(wait_for(lambda: goal_state(goal)["status"] == "active" or False,
  50. timeout=60, what="plan approved") is not None,
  51. "approving the plan activates the goal")
  52. check(wait_for(lambda: run_status(goal) == "suspended" or False,
  53. timeout=60, what="agent parks") is not None,
  54. "the approved agent parks (saddle suspend)")
  55. # The stall fixture: a draft that was never enqueued (the typo'd
  56. # enqueue class) — no terminal transition will ever fire.
  57. conn = db()
  58. agent_session = conn.execute(
  59. "select max(id) from sessions where task_id = ?",
  60. (run_task_of(goal),)).fetchone()[0]
  61. conn.close()
  62. status, body = saddle_api("POST", "/api/saddle/task/create", fake_token, {
  63. "session_id": agent_session, "title": "QA sweep",
  64. "mandate": "Run the QA sweep.", "mode": "test"})
  65. check(status == 200 and body.get("ok") is True,
  66. "the stall fixture draft task creates (%s)" % body.get("error"))
  67. stall_draft = int(body["task_id"])
  68. # The actionable error tail (goal 4 t1): the typo'd enqueue names
  69. # the recovery path while keeping the pinned substring. The wrong
  70. # id is a REAL task owned by nobody's goal (a plain human task):
  71. # the ownership check fires before the existence-only path.
  72. conn = db()
  73. conn.execute("insert into tasks (project_id, kind, title, status, priority,"
  74. " mode, creator, created_at) values (1, 'task', 'Wrong id',"
  75. " 'draft', 5, 'implement', 'human', strftime('%s','now'))")
  76. wrong_id = conn.execute("select max(id) from tasks").fetchone()[0]
  77. conn.commit()
  78. conn.close()
  79. status, body = saddle_api("POST", "/api/saddle/task/enqueue", fake_token, {
  80. "session_id": agent_session, "task_id": wrong_id})
  81. check(status == 400 and "does not belong to your goal" in (body.get("error") or "")
  82. and "nothing was enqueued" in (body.get("error") or "")
  83. and "saddle tasks" in (body.get("error") or ""),
  84. "the enqueue error carries the pinned substring + the actionable tail (%s)"
  85. % body.get("error"))
  86. state = goal_state(goal)
  87. check(state.get("force_wake") is not None,
  88. "an active goal with a parked run offers the force-wake action")
  89. goal_page = http_get_page(auth, "/goals/%d" % goal)
  90. check('stm-action="goal.force_wake"' in goal_page
  91. and 'stm-if="goal.force_wake != null"' in goal_page,
  92. "the goal page authors the gated Force wake button")
  93. run_page = http_get_page(auth, "/tasks/%d" % run_task_of(goal))
  94. check('stm-action="task.force_wake"' in run_page,
  95. "the suspended goal-run row offers the task-page wake (routing to the goal)")
  96. # No press, no wake: a quiet window passes without any resume.
  97. parked_runs = len(fake.goal_assignments)
  98. check(quiet_window(quiet_seconds(),
  99. lambda: len(fake.goal_assignments) == parked_runs,
  100. "no press, no wake"),
  101. "without a press the parked agent never wakes")
  102. # Press: the wake fires immediately (no coalesce wait) and leads
  103. # with the explicit line; the statuses name the draft as NOT
  104. # enqueued with the enqueue fix.
  105. act(auth, goal_state(goal)["force_wake"], {})
  106. check(wait_for(lambda: len(fake.goal_assignments) > parked_runs or False,
  107. timeout=30, what="force-woke agent resumes") is not None,
  108. "the force-woke agent resumes")
  109. woke_notice = fake.goal_assignments[-1].get("resume_notice") or ""
  110. check("force-woke this goal's supervision" in woke_notice
  111. and "no pending wake" in woke_notice,
  112. "the resume notice leads with the explicit force-woke line")
  113. check(("draft (NOT enqueued" in woke_notice)
  114. and (("saddle task enqueue %d" % stall_draft) in woke_notice),
  115. "the statuses list names the never-enqueued draft with the fix")
  116. check("Task statuses" in woke_notice,
  117. "the force-wake notice keeps the standard batched report")
  118. check(wait_for(lambda: run_status(goal) == "suspended" or False,
  119. timeout=60, what="woken agent re-parks") is not None,
  120. "the woken agent parks again (script)")
  121. # The flag is consumed: a second window stays quiet.
  122. woke_runs = len(fake.goal_assignments)
  123. check(quiet_window(quiet_seconds(),
  124. lambda: len(fake.goal_assignments) == woke_runs,
  125. "consumed flag never re-fires"),
  126. "the consumed flag does not re-wake (one wake per press)")
  127. print("== goal 4 t1: guards — paused goal, non-suspended run ==")
  128. sealed_wake = state["force_wake"]
  129. act(auth, goal_state(goal)["pause"], {})
  130. state = goal_state(goal)
  131. check(state.get("force_wake") is None,
  132. "a paused goal offers no force-wake action")
  133. # The server-side guard: replay the still-sealed force-wake action
  134. # captured while the goal was active — the paused goal refuses it
  135. # (error 72; the flag stays clear).
  136. replay = act(auth, sealed_wake, {})
  137. check(any(d.get("type") == "notify" and d.get("kind") == "error"
  138. and "force-woke" in (d.get("message") or "")
  139. for d in replay if isinstance(d, dict)),
  140. "the paused-goal guard refuses the replayed wake (%s)" % replay)
  141. conn = db()
  142. cleared = conn.execute("select force_wake from goals where id = ?",
  143. (goal,)).fetchone()
  144. conn.close()
  145. check(cleared is not None and cleared[0] in (0, False),
  146. "the goal force-wake flag stays clear after the guard refusal")
  147. act(auth, goal_state(goal)["resume"], {})
  148. check(wait_for(lambda: goal_state(goal)["status"] == "active" or False,
  149. timeout=30, what="goal reactivates") is not None,
  150. "the goal reactivates")
  151. print("== goal 4 t1: plain-task force wake on its affine saddle ==")
  152. conn = db()
  153. conn.execute("insert into tasks (project_id, kind, title, status, priority, mode,"
  154. " creator, created_at, suspended_at) values (1, 'task',"
  155. " 'Plain parked', 'suspended', 5, 'implement', 'human',"
  156. " strftime('%s','now'), strftime('%s','now'))")
  157. plain_parked = conn.execute("select max(id) from tasks").fetchone()[0]
  158. conn.execute("insert into sessions (task_id, saddle_id, started_at, ended_at,"
  159. " outcome) values (?, ?, strftime('%s','now'),"
  160. " strftime('%s','now'), 'parked')",
  161. (plain_parked, fake_id))
  162. conn.commit()
  163. conn.close()
  164. detail = task_state(auth, plain_parked)
  165. check(detail.get("force_wake") is not None,
  166. "a suspended plain task offers the force-wake action")
  167. task_page = http_get_page(auth, "/tasks/%d" % plain_parked)
  168. check('stm-action="task.force_wake"' in task_page
  169. and 'stm-if="task.force_wake != null"' in task_page,
  170. "the task page authors the gated Force wake button")
  171. status, _ = fake.ask_now()
  172. check(status == 204,
  173. "without a press the parked plain task is not offerable (204)")
  174. act(auth, task_state(auth, plain_parked)["force_wake"], {})
  175. status, resume_payload = fake.ask_now()
  176. check(status == 200 and int(resume_payload["task"]["id"]) == plain_parked,
  177. "the force-woke plain task is claimable again")
  178. notice = resume_payload.get("resume_notice") or ""
  179. check("force-woke this parked task" in notice
  180. and "resume where you left off" in notice,
  181. "the plain-task resume carries the explicit wake notice")
  182. conn = db()
  183. flag_row = conn.execute("select force_wake from tasks where id = ?",
  184. (plain_parked,)).fetchone()
  185. conn.close()
  186. check(flag_row is not None and flag_row[0] in (0, False),
  187. "the claim consumed the task force-wake flag")
  188. check(wait_task_done(auth, plain_parked, timeout=60) is not None,
  189. "the woken plain task completes")
  190. check(task_state(auth, plain_parked).get("force_wake") is None,
  191. "a done task offers no force-wake action")