diff --git a/src/agent_comms/standing_rules.py b/src/agent_comms/standing_rules.py index 31f328505b..3c93cb8bcf 100644 --- a/src/agent_comms/standing_rules.py +++ b/src/agent_comms/standing_rules.py @@ -593,8 +593,8 @@ def derive(cls, branches: tuple[HeldBranch, ...]) -> tuple[Self, ...]: ... @dataclass(frozen=True) class BranchBehindMain(CoreActsStandingRule, HeldBranchRule): - """A held branch needs a new base the moment main passes it. Syncing is the holder's - reflex, so Core tells the holder itself instead of waiting on a teammate relay.""" + """Outstanding branch changes need a new base when main passes them. Core tells + the holder directly; an empty historical checkout owes no new publication.""" problem: str holder: ThreadIncarnation @@ -620,7 +620,7 @@ def derive(cls, branches: tuple[HeldBranch, ...]) -> tuple[Self, ...]: return tuple(cls(problem=held.problem, holder=held.holder, repository=held.difference.repository, branch=held.difference.branch, behind=held.difference.behind) - for held in branches if held.difference.behind and not held.queued) + for held in branches if held.difference.needs_rebase and not held.queued) def line(self) -> str: return (f"REBASE {self.repository.to_wire()}:{self.branch} ({self.holder.name} {self.problem}) is" diff --git a/src/agent_comms/work_branches.py b/src/agent_comms/work_branches.py index b45f7b9787..6fe06c484f 100644 --- a/src/agent_comms/work_branches.py +++ b/src/agent_comms/work_branches.py @@ -220,6 +220,16 @@ class BranchDifference: growth: SourceGrowth unpublished: tuple[UnpushedHead, ...] + @property + def needs_rebase(self) -> bool: + """Only outstanding committed changes need a newer base before publication. + + An ancestor of main has no outgoing delta; retaining its checkout as evidence + does not create work whenever main advances. Uncommitted edits remain owned + by the checkout and the holder's before-commit synchronization. + """ + return bool(self.behind and self.files) + @property def changes_source(self) -> bool: """Any file under the source tree, not only the Python modules growth counts.""" diff --git a/tests/test_team_replay.py b/tests/test_team_replay.py index 5e345e7a15..c05944cf3d 100644 --- a/tests/test_team_replay.py +++ b/tests/test_team_replay.py @@ -154,7 +154,6 @@ def order(self, name: str, worktree: Path, *, at: float) -> Todo: name, CreateProblemGoalAction( problem=problem, - commander=self.commander, request=self.request, assigner=self.thread(self.commander).incarnation, repo=self.repository, @@ -472,6 +471,27 @@ def behind(name, number, queued): assert queued.id not in told +@pytest.mark.parametrize("outstanding", (False, True)) +def test_rebase_notice_requires_outstanding_branch_changes(fleet, outstanding): + built, _ = fleet + home = built.worktree("retained-history", base=built.old_main) + if outstanding: + built.commit(home, "repair.py", "repair = 1\n") + built.push(home) + todo = built.unit("history-worker", home) + storage = built.comms.registry.storage_rule + checkout = storage.order_checkout(storage.initial_roots("history-worker"), todo.id) + original = built.git(checkout, "rev-parse", "HEAD") + + for index in range(2): + built.commit(built.source.checkout, "main.py", f"count = {index + 1}\n") + built.git(built.source.checkout, "push", "-q", "origin", "main") + told = {rule.problem for rule in built.observe().rules + if isinstance(rule, BranchBehindMain)} + assert (todo.id in told) is outstanding + assert built.git(checkout, "rev-parse", "HEAD") == original + + def test_branch_rules_read_the_order_checkout_instead_of_the_behind_home(fleet): built, _ = fleet home = built.worktree('old-home', base=built.old_main)