mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-12 23:29:55 +00:00
Address action-demanding transitions to the assignee; neutral verdict label
Send-back/unblock/cancel by someone else now land in the worker's queue (only cancel did — send-backs woke nobody); done stays unaddressed. The Request changes button drops the send-to-X phrasing — it's a board write, delivery is the queue's job.
This commit is contained in:
+13
-4
@@ -545,12 +545,21 @@ class TeamStore:
|
|||||||
f"illegal transition {current.value} → {target.value}"
|
f"illegal transition {current.value} → {target.value}"
|
||||||
)
|
)
|
||||||
self._check_transition_authority(actor, item, current, target)
|
self._check_transition_authority(actor, item, current, target)
|
||||||
# Canceling an assigned item ADDRESSES the notice to its assignee — the
|
# When someone ELSE moves your assigned item to a state that needs
|
||||||
# top-priority queue entry whose delivery (or in-flight interrupt) makes
|
# YOUR action, the event is addressed to you: a send-back
|
||||||
# the worker actually stop, instead of finishing into the void.
|
# (review→in_progress with feedback), an unblock, a cancel (whose
|
||||||
|
# delivery/in-flight interrupt makes the worker actually stop). This
|
||||||
|
# is a board write, not a message — delivery is the queue projection
|
||||||
|
# doing its job. DONE is deliberately unaddressed: waking a worker to
|
||||||
|
# say its finished item is finished would burn a turn for nothing.
|
||||||
|
action_needed = {
|
||||||
|
ItemState.IN_PROGRESS,
|
||||||
|
ItemState.BLOCKED,
|
||||||
|
ItemState.CANCELED,
|
||||||
|
}
|
||||||
recipient = (
|
recipient = (
|
||||||
item["assignee"]
|
item["assignee"]
|
||||||
if target is ItemState.CANCELED
|
if target in action_needed
|
||||||
and item["assignee"]
|
and item["assignee"]
|
||||||
and item["assignee"] != actor.id
|
and item["assignee"] != actor.id
|
||||||
else None
|
else None
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ test("item detail: timeline with attachment, worker link, request changes", asyn
|
|||||||
// Request changes… discloses a comment box; sending returns the item to work
|
// Request changes… discloses a comment box; sending returns the item to work
|
||||||
await detail.getByRole("button", { name: "Request changes…" }).click();
|
await detail.getByRole("button", { name: "Request changes…" }).click();
|
||||||
await detail.getByPlaceholder("What needs to change?").fill("totals drift on Tom");
|
await detail.getByPlaceholder("What needs to change?").fill("totals drift on Tom");
|
||||||
await detail.getByRole("button", { name: "Send to security" }).click();
|
await detail.getByRole("button", { name: "Request changes", exact: true }).click();
|
||||||
await expect(detail).toContainText("In progress");
|
await expect(detail).toContainText("In progress");
|
||||||
// switching rows switches the pane
|
// switching rows switches the pane
|
||||||
await page.getByTestId("board-item-3").click();
|
await page.getByTestId("board-item-3").click();
|
||||||
|
|||||||
@@ -358,6 +358,8 @@ function DetailActions({
|
|||||||
onChange={(e) => setChangesText(e.target.value)}
|
onChange={(e) => setChangesText(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="board-changes-row">
|
<div className="board-changes-row">
|
||||||
|
{/* A board write, not a message: review → in_progress with the
|
||||||
|
comment attached; delivery to the assignee is the queue's job. */}
|
||||||
<button
|
<button
|
||||||
className="board-btn primary"
|
className="board-btn primary"
|
||||||
disabled={!changesText.trim()}
|
disabled={!changesText.trim()}
|
||||||
@@ -365,7 +367,7 @@ function DetailActions({
|
|||||||
onTransition(detail.id, "in_progress", changesText.trim())
|
onTransition(detail.id, "in_progress", changesText.trim())
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Send to {detail.assignee || "worker"}
|
Request changes
|
||||||
</button>
|
</button>
|
||||||
<button className="board-btn ghost" onClick={() => setChangesOpen(false)}>
|
<button className="board-btn ghost" onClick={() => setChangesOpen(false)}>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -408,6 +408,27 @@ def test_item_detail_timeline_and_blocker_fact(manager):
|
|||||||
assert blocked["blocker"] == "need the staging tfvars"
|
assert blocked["blocker"] == "need the staging tfvars"
|
||||||
|
|
||||||
|
|
||||||
|
def test_transitions_by_others_address_the_assignee(store):
|
||||||
|
"""A send-back or unblock is a board write, not a message — but the queue
|
||||||
|
projection addresses it to the worker whose action it demands (owner
|
||||||
|
question 2026-08-17 exposed the gap: only cancel was addressed)."""
|
||||||
|
item_id = assigned(store)
|
||||||
|
store.consume("swe-worker", store.pending_for("swe-worker")[-1]["seq"])
|
||||||
|
store.transition(SPACE, WORKER, item_id, "in_progress")
|
||||||
|
store.transition(SPACE, WORKER, item_id, "review", comment="ready")
|
||||||
|
assert store.pending_for("swe-worker") == [] # own moves never self-address
|
||||||
|
# the lead's send-back reaches the worker's queue, feedback attached
|
||||||
|
store.transition(SPACE, LEAD, item_id, "in_progress", comment="totals drift")
|
||||||
|
pending = store.pending_for("swe-worker")
|
||||||
|
assert [e["payload"]["to"] for e in pending] == ["in_progress"]
|
||||||
|
assert pending[-1]["payload"]["comment"] == "totals drift"
|
||||||
|
store.consume("swe-worker", pending[-1]["seq"])
|
||||||
|
# ...but done is deliberately unaddressed — no wake to hear "it's finished"
|
||||||
|
store.transition(SPACE, WORKER, item_id, "review", comment="fixed")
|
||||||
|
store.transition(SPACE, LEAD, item_id, "done")
|
||||||
|
assert store.pending_for("swe-worker") == []
|
||||||
|
|
||||||
|
|
||||||
def test_cancel_notice_is_addressed_to_the_assignee(store):
|
def test_cancel_notice_is_addressed_to_the_assignee(store):
|
||||||
item_id = assigned(store)
|
item_id = assigned(store)
|
||||||
store.consume("swe-worker", store.pending_for("swe-worker")[-1]["seq"])
|
store.consume("swe-worker", store.pending_for("swe-worker")[-1]["seq"])
|
||||||
|
|||||||
Reference in New Issue
Block a user