refactor(content): move orchestration markers off quick_context to typed jsonb

This commit is contained in:
Renn F
2026-06-21 02:48:07 +02:00
parent 7e74112fcc
commit 0fba4eed01
21 changed files with 270 additions and 316 deletions
+34 -55
View File
@@ -1,21 +1,15 @@
"""Supersede umbrella close-on-land guards.
"""Supersede umbrella close-on-land guards (orchestration-marker storage).
Covers the parts of the external-PR supersede flow that decide whether — and
which — a landed supersede's contributor PR gets retired:
- ``supersede_marker_line`` anchors marker/state checks to the marker line, so
free-form CEO escalation/approval notes appended to the same multi-writer
``quick_context`` can't be mistaken for the marker.
- ``supersede_umbrellas_pending_close`` only returns umbrellas whose
replacement work actually landed (a non-cancelled descendant carrying a PR),
not every COMPLETED umbrella — the CEO can force-complete over a cancelled
code subtask.
- ``mark_supersede_pr_closed`` writes the ``closed=1`` idempotency token onto
the marker line, surviving appended notes.
The supersede marker (``pr={n} review={uuid}`` plus a ``closed=1`` token once
the contributor PR is retired) lives in ``orchestration_markers`` after
migration 041 — isolated from the human ``quick_context``, so CEO escalation /
approval notes can no longer be mistaken for it.
"""
from __future__ import annotations
from types import SimpleNamespace
from typing import Any
from unittest.mock import AsyncMock, MagicMock
from uuid import uuid4
@@ -23,11 +17,10 @@ import pytest
from roboco.models.base import TaskStatus
from roboco.services.task import TaskService, supersede_marker_line
_MARKER = "external_pr_supersede pr=5 review=abc"
_VALUE = "pr=5 review=abc"
def _scalars_all(rows: list[object]) -> MagicMock:
"""A session.execute return value whose .scalars().all() yields `rows`."""
res = MagicMock()
res.scalars.return_value.all.return_value = rows
return res
@@ -44,25 +37,23 @@ def _bind(svc: TaskService, name: str, value: object) -> None:
object.__setattr__(svc, name, value)
def _task(supersede: str | None = None, **kw: Any) -> SimpleNamespace:
om = {"external_pr_supersede": supersede} if supersede is not None else None
return SimpleNamespace(orchestration_markers=om, **kw)
# ---------------------------------------------------------------------------
# supersede_marker_line — line anchoring
# supersede_marker_line — reads the marker value
# ---------------------------------------------------------------------------
def test_marker_line_returns_marker_ignoring_appended_notes() -> None:
qc = f"{_MARKER}\nceo_approval_notes: shipped, looks good"
assert supersede_marker_line(qc) == _MARKER
def test_marker_line_not_fooled_by_closed_token_in_note() -> None:
qc = f"{_MARKER}\nceo_approval_notes: marked closed=1 in jira"
# The marker line itself carries no closed=1, so the PR is NOT yet closed.
assert "closed=1" not in supersede_marker_line(qc).split()
def test_marker_line_returns_value() -> None:
assert supersede_marker_line(_task(_VALUE)) == _VALUE
def test_marker_line_empty_when_absent() -> None:
assert supersede_marker_line("no marker here\nescalation_notes: x") == ""
assert supersede_marker_line(None) == ""
assert supersede_marker_line(_task()) == ""
assert supersede_marker_line(SimpleNamespace(orchestration_markers=None)) == ""
# ---------------------------------------------------------------------------
@@ -72,29 +63,25 @@ def test_marker_line_empty_when_absent() -> None:
@pytest.mark.asyncio
async def test_pending_close_excludes_umbrella_with_closed_marker() -> None:
umbrella = MagicMock(id=uuid4(), quick_context=f"{_MARKER} closed=1")
umbrella = _task(f"{_VALUE} closed=1", id=uuid4())
svc = _service(_scalars_all([umbrella]))
_bind(svc, "_supersede_replacement_landed", AsyncMock(return_value=True))
assert await svc.supersede_umbrellas_pending_close() == []
@pytest.mark.asyncio
async def test_pending_close_keeps_umbrella_with_closed_token_only_in_note() -> None:
# A CEO note containing the literal "closed=1" must NOT retire the PR.
umbrella = MagicMock(
id=uuid4(), quick_context=f"{_MARKER}\nceo_approval_notes: closed=1 elsewhere"
)
async def test_pending_close_keeps_open_landed_umbrella() -> None:
umbrella = _task(_VALUE, id=uuid4())
svc = _service(_scalars_all([umbrella]))
_bind(svc, "_supersede_replacement_landed", AsyncMock(return_value=True))
out = await svc.supersede_umbrellas_pending_close()
assert out == [umbrella]
assert await svc.supersede_umbrellas_pending_close() == [umbrella]
@pytest.mark.asyncio
async def test_pending_close_requires_landed_replacement() -> None:
# COMPLETED + no closed marker, but the replacement never landed (the code
# subtask was cancelled) — close-on-land must skip it.
umbrella = MagicMock(id=uuid4(), quick_context=_MARKER)
umbrella = _task(_VALUE, id=uuid4())
svc = _service(_scalars_all([umbrella]))
_bind(svc, "_supersede_replacement_landed", AsyncMock(return_value=False))
assert await svc.supersede_umbrellas_pending_close() == []
@@ -116,7 +103,6 @@ async def test_replacement_landed_true_for_completed_descendant_with_pr() -> Non
async def test_replacement_landed_false_when_descendant_cancelled() -> None:
child = MagicMock(id=uuid4(), status=TaskStatus.CANCELLED, pr_number=42)
svc = _service(_scalars_all([child]))
# The `seen` guard terminates the walk even though the mock re-returns child.
assert await svc._supersede_replacement_landed(uuid4()) is False
@@ -128,44 +114,37 @@ async def test_replacement_landed_false_when_completed_without_pr() -> None:
# ---------------------------------------------------------------------------
# find_supersede_umbrella — marker-line dedup
# find_supersede_umbrella — value dedup (pr=N review= prefix match)
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_find_umbrella_matches_marker_not_note() -> None:
match = MagicMock(id=uuid4(), quick_context=f"{_MARKER}\nescalation_notes: x")
other = MagicMock(
id=uuid4(),
# marker for a different PR, but a note mentions "pr=5 review=" text
quick_context="external_pr_supersede pr=9 review=z\nnote: see pr=5 review= ok",
)
async def test_find_umbrella_matches_by_value() -> None:
match = _task(_VALUE, id=uuid4())
other = _task("pr=9 review=z", id=uuid4()) # different PR
svc = _service(_scalars_all([other, match]))
found = await svc.find_supersede_umbrella(uuid4(), 5)
assert found is match
# ---------------------------------------------------------------------------
# mark_supersede_pr_closed — token written on the marker line
# mark_supersede_pr_closed — token appended to the marker value
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_mark_closed_appends_token_to_marker_line() -> None:
task = MagicMock(quick_context=f"{_MARKER}\nceo_approval_notes: shipped")
async def test_mark_closed_appends_token() -> None:
task = _task(_VALUE)
svc = _service(_scalars_all([]))
_bind(svc, "get", AsyncMock(return_value=task))
await svc.mark_supersede_pr_closed(uuid4())
lines = task.quick_context.splitlines()
assert lines[0] == f"{_MARKER} closed=1"
assert lines[1] == "ceo_approval_notes: shipped" # note untouched
assert supersede_marker_line(task) == f"{_VALUE} closed=1"
@pytest.mark.asyncio
async def test_mark_closed_is_idempotent_on_marker_line() -> None:
task = MagicMock(quick_context=f"{_MARKER} closed=1\nceo_approval_notes: x")
async def test_mark_closed_is_idempotent() -> None:
task = _task(f"{_VALUE} closed=1")
svc = _service(_scalars_all([]))
_bind(svc, "get", AsyncMock(return_value=task))
await svc.mark_supersede_pr_closed(uuid4())
# No second closed=1 token appended.
assert task.quick_context.splitlines()[0] == f"{_MARKER} closed=1"
assert supersede_marker_line(task) == f"{_VALUE} closed=1"