|
|
@@ -70,6 +70,14 @@ def run(ctx):
|
|
|
"the task parks as blocked")
|
|
|
check(saddle_card(auth, "resume-sleeper")["status"] == "idle",
|
|
|
"the parked session's saddle row reads idle")
|
|
|
+ conn = db()
|
|
|
+ parked_row = conn.execute(
|
|
|
+ "select outcome, ended_at is not null from sessions where id = ?",
|
|
|
+ (sleeping_session,)).fetchone()
|
|
|
+ conn.close()
|
|
|
+ check(tuple(parked_row) == ("parked", 1),
|
|
|
+ "the park closes its session row 'parked' at park time (D4: the row"
|
|
|
+ " aligns with the daemon's file-log park bracket)")
|
|
|
ask_status, _ = sleeper.ask_now()
|
|
|
check(ask_status == 204,
|
|
|
"the parked daemon's ask returns 204 — it sleeps until woken")
|
|
|
@@ -87,8 +95,8 @@ def run(ctx):
|
|
|
"answering the block wakes the sleeping affine saddle")
|
|
|
# Answering = allocating (plan 11): the resume assignment carries
|
|
|
# the verdict as resume_notice (stable wording) AND a FRESH
|
|
|
- # session id — the parked row closed, the daemon keys its resumed
|
|
|
- # pushes on the new one.
|
|
|
+ # session id — the parked row closed 'parked' at its park (D4),
|
|
|
+ # the daemon keys its resumed pushes on the new one.
|
|
|
check(wait_for(lambda: any(
|
|
|
int(p.get("task", {}).get("id") or 0) == sleeping_task
|
|
|
and p.get("session_id") != sleeping_session
|
|
|
@@ -113,10 +121,10 @@ def run(ctx):
|
|
|
"select id, outcome, ended_at is not null from sessions where task_id = ?"
|
|
|
" order by id", (sleeping_task,)).fetchall()
|
|
|
conn.close()
|
|
|
- check(len(rows) == 2 and rows[0][1] == "resumed" and rows[0][2]
|
|
|
+ check(len(rows) == 2 and rows[0][1] == "parked" and rows[0][2]
|
|
|
and rows[1][1] == "done" and rows[1][2],
|
|
|
- "parked session closed 'resumed'; the resumed round ran on a fresh row to done (%s)"
|
|
|
- % (rows,))
|
|
|
+ "parked session closed 'parked'; the resumed round ran on a fresh row"
|
|
|
+ " to done (%s)" % (rows,))
|
|
|
retire_daemon(sleeper, "resume-sleeper")
|
|
|
|
|
|
print("== phase 2: answer resume — the parked daemon is woken (POST /answer retired) ==")
|
|
|
@@ -210,10 +218,10 @@ def run(ctx):
|
|
|
"select id, outcome from sessions where task_id = ? order by id",
|
|
|
(parked_task,)).fetchall()
|
|
|
conn.close()
|
|
|
- check(row is not None and row[0] == "resumed"
|
|
|
- and len(session_rows) == 2 and session_rows[0] == (parked_session, "resumed"),
|
|
|
- "the parked session closed 'resumed' and the resumed round kept its own row (%s)"
|
|
|
- % (session_rows,))
|
|
|
+ check(row is not None and row[0] == "parked"
|
|
|
+ and len(session_rows) == 2 and session_rows[0] == (parked_session, "parked"),
|
|
|
+ "the parked session closed 'parked' (at its park, D4) and the resumed round"
|
|
|
+ " kept its own row (%s)" % (session_rows,))
|
|
|
retire_daemon(parked, "resume-parked")
|
|
|
|
|
|
print("== plan 11: plan acceptance — accept / reject / auto-approve ==")
|
|
|
@@ -296,13 +304,12 @@ def run(ctx):
|
|
|
|
|
|
print("== phase 2: offline requeue keeps parked-with-answered tasks ==")
|
|
|
# A daemon dying while its parked task carries an answered
|
|
|
- # verdict: going offline must not requeue the task or close its
|
|
|
- # session — the resume machinery (delivery once the daemon
|
|
|
- # returns, or the affinity allocation on its next ask) needs
|
|
|
- # both. The question park leaves the row idle; the review park
|
|
|
- # keeps it busy@task (`saddle review` blocks the round without
|
|
|
- # a parking state push), exercising the unreachable path's
|
|
|
- # requeue decision both ways.
|
|
|
+ # verdict: going offline must not requeue the task — the resume
|
|
|
+ # machinery (delivery once the daemon returns, or the affinity
|
|
|
+ # allocation on its next ask) needs the parked record (the
|
|
|
+ # session row closed 'parked' at its park, D4) and the task left
|
|
|
+ # parked. Both park kinds (question and review) exercise the
|
|
|
+ # unreachable path's requeue decision.
|
|
|
stranded = answerflow_daemon()
|
|
|
register_answerflow(stranded, "resume-stranded")
|
|
|
stranded_task = create_answerflow_task("Stranded answered task")
|
|
|
@@ -329,12 +336,13 @@ def run(ctx):
|
|
|
"stranded task not requeued"),
|
|
|
"the parked-with-answered task is not requeued by the offline pass")
|
|
|
conn = db()
|
|
|
- open_session = conn.execute(
|
|
|
- "select count(*) from sessions where task_id = ? and ended_at is null",
|
|
|
- (stranded_task,)).fetchone()[0]
|
|
|
+ stranded_rows = conn.execute(
|
|
|
+ "select outcome, ended_at is not null from sessions where task_id = ?"
|
|
|
+ " order by id desc limit 1", (stranded_task,)).fetchone()
|
|
|
conn.close()
|
|
|
- check(open_session == 1,
|
|
|
- "the parked-with-answered task keeps its open session for the resume")
|
|
|
+ check(tuple(stranded_rows) == ("parked", 1),
|
|
|
+ "the parked-with-answered task keeps its parked session record ('parked')"
|
|
|
+ " for the resume")
|
|
|
|
|
|
reviewer = answerflow_daemon(ask_on_terminal=False)
|
|
|
register_answerflow(reviewer, "resume-reviewer")
|
|
|
@@ -370,12 +378,13 @@ def run(ctx):
|
|
|
"review-parked task not requeued"),
|
|
|
"the review-parked task with an answered verdict is never requeued")
|
|
|
conn = db()
|
|
|
- open_session = conn.execute(
|
|
|
- "select count(*) from sessions where task_id = ? and ended_at is null",
|
|
|
- (review_task,)).fetchone()[0]
|
|
|
+ review_rows = conn.execute(
|
|
|
+ "select outcome, ended_at is not null from sessions where task_id = ?"
|
|
|
+ " order by id desc limit 1", (review_task,)).fetchone()
|
|
|
conn.close()
|
|
|
- check(open_session == 1,
|
|
|
- "the review-parked task keeps its open session for the resume")
|
|
|
+ check(tuple(review_rows) == ("parked", 1),
|
|
|
+ "the review-parked task keeps its parked session record ('parked')"
|
|
|
+ " for the resume")
|
|
|
|
|
|
print("== phase 2: staleness — one probe decides ==")
|
|
|
# A silent-but-alive runner (long think, nothing to flush): after
|