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
9 changes: 9 additions & 0 deletions docs/status-data-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,15 @@ goal-level refresh in a multi-agent goal must use a registered peer with
Use agent-lane scope for a peer-local recommendation that should not replace the
durable goal route.

`connected_without_run` means a connected Goal has no recorded runs. When all
recorded runs are agent-lane notes or status-neutral events, `latest_status_run`
remains null; this does not mean the Goal never ran. Attention uses the current
authoritative active-state Todo projection when available, otherwise it retains
`unknown`. The full-history `unique_runs` count preserves this distinction when
the displayed `latest_runs` window is empty. Neither history nor an open Todo
proves a host is currently executing; Agent execution state still requires its
separate lane, worker and lease facts.

If a peer self-merged slice materially advanced the public product or case
path, that peer or another registered peer should also write a project-level
refresh with the matching `delivery_outcome=outcome_progress` (or skip the
Expand Down
6 changes: 5 additions & 1 deletion loopx/control_plane/work_items/attention_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,11 @@ def build_attention_queue(
item = active_state_item
else:
item = context.goal_attention(goal)
if not item:
if not item or (
current_status_run is None
and item.get("status") == "unknown"
and active_state_item
):
item = active_state_item
if item:
item, projection_warning = _reconcile_active_state_item(
Expand Down
10 changes: 8 additions & 2 deletions loopx/control_plane/work_items/attention_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,18 @@ def goal_attention(
adapter_status in connected_adapter_statuses
or adapter_status in connected_delivery_adapter_statuses
):
# Lane notes and neutral runs do not establish Goal status, but are history.
has_history = bool(goal.get("unique_runs") or goal.get("latest_runs"))
return attention_item(
goal_id=goal_id,
status="connected_without_run",
status="unknown" if has_history else "connected_without_run",
waiting_on="codex",
severity="action",
recommended_action="run the first read-only adapter tick and save a compact run record",
recommended_action=(
"inspect the active goal state; existing history has no goal-level status run"
if has_history
else "run the first read-only adapter tick and save a compact run record"
),
source="run_history",
**lifecycle_fields,
)
Expand Down
104 changes: 104 additions & 0 deletions tests/control_plane/test_status_no_run_projection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""Missing Goal-level status is distinct from missing execution history."""
from __future__ import annotations

import json

import pytest

from canonical_authority_fixture import initialize_canonical_source_fixture
from loopx.status import collect_status, goal_attention, latest_run


GOAL_RUN = {
"classification": "state_refreshed",
"recommended_action": "Inspect the current deliverable.",
"json_exists": True,
"markdown_exists": True,
}
LANE_RUN = {**GOAL_RUN, "progress_scope": "agent_lane", "agent_id": "peer"}
CONNECTED_GOAL = {"id": "goal-a", "adapter_status": "connected-read-only"}


@pytest.mark.parametrize("history", [
{"latest_status_run": GOAL_RUN, "latest_runs": [LANE_RUN]},
{"latest_runs": [LANE_RUN, GOAL_RUN]},
])
def test_new_and_legacy_run_fields_keep_goal_status(history):
goal = {**CONNECTED_GOAL, **history}
assert latest_run(goal) == GOAL_RUN
assert goal_attention(goal)["status"] == "state_refreshed"


@pytest.mark.parametrize("history", [
{"latest_status_run": None, "latest_runs": [LANE_RUN], "unique_runs": 1},
{"latest_runs": [LANE_RUN]},
{"latest_runs": [{"classification": "quota_slot_spent"}]},
{"latest_status_run": None, "latest_runs": [], "unique_runs": 4},
])
def test_history_without_goal_status_remains_unknown(history):
goal = {**CONNECTED_GOAL, **history}
assert latest_run(goal) is None
item = goal_attention(goal)
assert item["status"] == "unknown"
assert item["source"] == "run_history"


@pytest.mark.parametrize("history", [
{},
{"latest_status_run": None, "latest_runs": [], "unique_runs": 0},
])
def test_genuinely_unrun_connected_goal_keeps_initial_tick(history):
assert goal_attention({**CONNECTED_GOAL, **history})["status"] == "connected_without_run"


def test_unclassified_goal_run_stays_unknown():
run = {"json_exists": True, "markdown_exists": True}
item = goal_attention({**CONNECTED_GOAL, "latest_status_run": run})
assert item["status"] == "unknown"
assert item["source"] == "latest_run"


@pytest.mark.parametrize("role,expected", [
(None, "unknown"),
("agent", "active_state_agent_todo"),
("user", "active_state_user_gate"),
])
def test_status_uses_canonical_work_without_promoting_agent_history(tmp_path, role, expected):
registry, runtime, state = tmp_path / "registry.json", tmp_path / "runtime", tmp_path / "state.md"
state.write_text(
"# Goal\n\n## Agent Todo\n\n" + (
("## User Todo\n\n" if role == "user" else "") +
"- [ ] Inspect the current deliverable.\n"
" <!-- loopx:todo todo_id=todo_work status=open " +
("task_class=user_gate" if role == "user" else "claimed_by=peer") + " -->\n"
if role else ""
), encoding="utf-8",
)
registry.write_text(json.dumps({
"schema_version": 1, "common_runtime_root": str(runtime),
"goals": [{"id": "goal-a", "repo": str(tmp_path), "state_file": str(state),
"adapter": {"kind": "read_only_project_map_v0", "status": "connected-read-only"},
"coordination": {"registered_agents": ["peer"]}}],
}), encoding="utf-8")
initialize_canonical_source_fixture(runtime, "goal-a", state_path=state, provider="sqlite")
runs = runtime / "goals/goal-a/runs"
runs.mkdir(parents=True, exist_ok=True)
artifact = runs / "lane.json"
artifact.write_text("{}", encoding="utf-8")
index = runs / "index.jsonl"
index.write_text(json.dumps({
**LANE_RUN, "goal_id": "goal-a", "generated_at": "2026-01-01T00:00:00Z",
"json_path": str(artifact), "markdown_path": str(artifact),
}) + "\n", encoding="utf-8")
before = {path: path.read_bytes() for path in (registry, state, artifact, index)}

payload = collect_status(registry_path=registry, runtime_root_override=str(runtime),
goal_id="goal-a", limit=1)

item, = (row for row in payload["attention_queue"]["items"] if row["goal_id"] == "goal-a")
assert item["status"] == expected
assert item["source"] == ("active_state" if role else "run_history")
if role == "user":
assert item["waiting_on"] == "controller"
assert payload["run_history"]["goals"][0]["latest_status_run"] is None
assert {path: path.read_bytes() for path in before} == before
Loading