Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/normal_day.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,6 @@ def _handle_ticket_progress(
ticket["causal_chain"] = chain.snapshot()
ticket["updated_at"] = current_actor_time_iso

self._save_ticket(ticket)

for pr_id in ticket.get("linked_prs", []):
pr = self._mem._prs.find_one({"pr_id": pr_id, "status": "open"}, {"_id": 0})
if pr and pr.get("changes_requested"):
Expand All @@ -433,6 +431,8 @@ def _handle_ticket_progress(
ticket["in_review_since"] = self._state.day
self._mem.upsert_pr(pr)

self._save_ticket(ticket)

ticket_body = "\n".join(
filter(
None,
Expand Down Expand Up @@ -555,7 +555,7 @@ def _try_force_merge_stale_pr(
review_age >= 5
and bool(linked_prs)
and actor_clock_ok
and (not open_pr_with_changes or review_age >= 7)
and not open_pr_with_changes
)

if force_merge:
Expand Down
73 changes: 10 additions & 63 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,9 @@ def test_stale_pr_force_merged_after_5_days(
"PR stale for >= 5 days with no changes_requested must be force-merged"
)

ticket = sim._mem.get_ticket("ENG-103")
assert ticket["status"] == "Done", "Ticket must be marked Done when PR is force-merged."

@patch("normal_day.Crew")
@patch("normal_day.Task")
@patch("agent_factory.Agent")
Expand Down Expand Up @@ -573,16 +576,13 @@ def test_stale_pr_with_changes_requested_is_never_force_merged(
@patch("normal_day.Crew")
@patch("normal_day.Task")
@patch("agent_factory.Agent")
def test_assignee_progress_clears_changes_requested_flag(
def test_assignee_progress_clears_changes_requested_flag_and_updates_ticket(
self, mock_agent, mock_task, mock_crew, sim
):
"""
After the assignee works on a ticket that has a PR with changes_requested,
that flag must be cleared so the PR becomes eligible for force-merge again.
"""
author = ALL_NAMES[0]

_seed_ticket(
sim._mem, "ENG-111", author, status="In Review", linked_prs=["PR-111"]
sim._mem, "ENG-111", author, status="In Progress", linked_prs=["PR-111"]
)
_seed_pr(sim._mem, "PR-111", "ENG-111", author, [ALL_NAMES[1]])
sim._mem._prs.update_one(
Expand Down Expand Up @@ -615,67 +615,14 @@ def test_assignee_progress_clears_changes_requested_flag(

pr = sim._mem._prs.find_one({"pr_id": "PR-111"}, {"_id": 0})
assert not pr.get("changes_requested"), (
"Assignee addressing feedback must clear changes_requested "
"so the PR can re-enter the review/force-merge cycle"
"PR changes_requested flag should be cleared."
)

@patch("normal_day.Crew")
@patch("normal_day.Task")
@patch("agent_factory.Agent")
def test_stale_pr_with_changes_requested_force_merges_after_escape_hatch(
self, mock_agent, mock_task, mock_crew, sim
):
"""
A PR stuck with changes_requested for >= 7 days must eventually be
force-merged via the time-based escape hatch (review_age >= 7 overrides
the changes_requested guard). Without this, a PR whose reviewer goes
silent is blocked forever.
"""
author = ALL_NAMES[0]
_seed_ticket(
sim._mem, "ENG-112", author, status="In Review", linked_prs=["PR-112"]
)
_seed_pr(sim._mem, "PR-112", "ENG-112", author, [ALL_NAMES[1]])
sim._mem._prs.update_one(
{"pr_id": "PR-112"}, {"$set": {"changes_requested": True}}
ticket = sim._mem.get_ticket("ENG-111")
assert ticket["status"] == "In Review", (
"Ticket status should be advanced back to 'In Review'."
)

sim._mem._jira.update_one(
{"id": "ENG-112"}, {"$set": {"in_review_since": sim.state.day - 8}}
)
mock_crew.return_value = _make_pr_review_crew()

org_plan = _make_org_plan(
sim,
{
"Engineering_Mobile": [
EngineerDayPlan(
name=author,
dept="Engineering_Mobile",
agenda=[
AgendaItem(
activity_type="ticket_progress",
description="Force merge after reviewer silence",
related_id="ENG-112",
estimated_hrs=0.5,
)
],
stress_level=20,
)
]
},
)

sim._normal_day.handle(org_plan)

pr = sim._mem._prs.find_one({"pr_id": "PR-112"}, {"_id": 0})
assert pr["status"] == "merged", (
"PR stuck with changes_requested for >= 7 days must be force-merged "
"via the time-based escape hatch"
)
ticket = sim._mem.get_ticket("ENG-112")
assert ticket["status"] == "Done"


class TestPlannerInReviewSection:
"""
Expand Down
Loading