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: 5 additions & 1 deletion docs/architecture/rfcs/harness-selection-dsh-pi-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ A proposal of kind `steward_team_plan_preview` (`steward_team_plan_preview_v0`)
is validated before anything may be applied, and a validated preview names, and
may not invent:

- the exact Goal the plan staffs, so the admission that validates its lanes and
the settlement that materializes them describe one Goal rather than two;
- each lane and the Agent that runs it, resolved from the Agents Core already
registers for the Goal, at most 8 lanes;
- that lane's first bounded Todo, with its declared priority (P0..P3), task
Expand Down Expand Up @@ -529,7 +531,9 @@ Shipped enforcement, in delivery order:
kind at `PRE_SETTLEMENT`. The apply re-validates the proposal against the
Goal's registered Agents and the shipped advancement action kinds, creates
the first bounded Todo of each *ready* lane through the canonical Todo owner,
creates nothing for a gap lane, and refuses an unknown Goal before any write.
creates nothing for a gap lane, refuses an unknown Goal before any write, and
refuses a plan whose named Goal differs from its settlement, so a plan
admitted against one Goal's Agents cannot be retargeted into another's.
The receipt records the proposal digest, so a replayed settlement reuses the
same lane Todo instead of adding a second row, and the receipt names every
lane Todo the settlement ensured.
Expand Down
8 changes: 6 additions & 2 deletions docs/architecture/rfcs/harness-selection-dsh-pi-v0.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ Chat Turn 也早已把 `response.proposals` 投影成 `proposal.ready` 事件。
kind 为 `steward_team_plan_preview`(`steward_team_plan_preview_v0`)的提案,在任何落地
之前先被校验;校验通过的预览必须点名、且不得编造:

- 这次配人的**确切 Goal**,使"验证 lanes 的准入"与"建成 lanes 的结算"描述的是同一个 Goal
而不是两个;
- 每条 lane 及其运行的 Agent,且只能来自 Core 已为该 Goal 注册的 Agent,最多 8 条 lane;
- 该 lane 的首个有界 Todo,含其声明优先级(P0..P3)、task class 与 action kind;
- 约束这些 lane 的 quota 包络;
Expand All @@ -413,8 +415,10 @@ Todo 创建、quota 或 goal policy——复用预览点名的身份,不得扩
3. **落地**(`#4524`,`c159a15b3`):受治理提案所有者在 `PRE_SETTLEMENT` 相位分派该 kind,
落地时重新按本 Goal 已注册 Agent 与本机 shipment 的 advancement action kind 校验,经
canonical Todo owner 为每条 **ready** lane 创建首个有界 Todo,gap lane 不创建任何东西,
未知 Goal 在任何写入前就被拒绝,回执记录 proposal digest,因此重放结算复用同一条 lane
Todo 而不会新增第二行,并且回执点名这次确保的每一条 lane Todo。
未知 Goal 在任何写入前就被拒绝,且**点名 Goal 与结算 Goal 不一致的计划会被拒绝**,因此
按某个 Goal 的 Agent 通过准入的计划无法被改投到另一个 Goal;回执记录 proposal digest,
因此重放结算复用同一条 lane Todo 而不会新增第二行,并且回执点名这次确保的每一条 lane
Todo。

这条入端口径目前在线上仍是**惰性**的,本节不作相反声明:还没有任何生产调用方传入
`team_plan_context`,因此模型产出的预览会在准入处被丢弃,而不会浮现给业主确认;提供准入事实
Expand Down
30 changes: 23 additions & 7 deletions loopx/control_plane/work_items/governed_transition_proposal.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,19 +263,26 @@ def _apply_team_plan(
) -> dict[str, Any]:
"""Create the confirmed lanes' first bounded Todos through the Todo owner.

The plan is re-validated here against this Goal's registered Agents and the
shipped advancement action kinds, so a proposal cannot become work by
bypassing admission. Only lanes the preview already marked ready are
materialized; a lane the preview reported as a gap stays a gap and creates
nothing, and the canonical Todo owner decides whether a row is added or
reused, which makes a replayed settlement idempotent.
"""
The plan is re-validated here against this Goal's registered Agents and the
shipped advancement action kinds, so a proposal cannot become work by
bypassing admission. Only lanes the preview already marked ready are
materialized; a lane the preview reported as a gap stays a gap and creates
nothing, and the canonical Todo owner decides whether a row is added or
reused, which makes a replayed settlement idempotent.
"""

from ...agent_registry import registered_agent_ids_for_goal
from ...history import load_registry
from ...registry import registry_goals
from ..todos.contract import TODO_ACTION_KIND_ADVANCEMENT_VALUES

# The plan names the Goal it staffs, and it may not be retargeted by the
# settlement it arrives in: admitting a plan against one Goal's agents and
# then creating its lanes under another would be a silent widening.
if str(proposal.get("goal_id") or "") != goal_id:
raise ValueError(
"steward team plan proposal names a different Goal than its settlement"
)
registry = load_registry(registry_path)
goal = next(
(
Expand Down Expand Up @@ -461,6 +468,7 @@ def settle_governed_transition_proposals(
STEWARD_TEAM_PLAN_PREVIEW_SCHEMA_VERSION = "steward_team_plan_preview_v0"
STEWARD_TEAM_PLAN_LANE_LIMIT = 8
STEWARD_TEAM_PLAN_PRIORITIES = ("P0", "P1", "P2", "P3")
_GOAL_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]{0,159}$")
STEWARD_TEAM_PLAN_GAP_REASONS = (
"agent_not_registered",
"capability_not_granted",
Expand Down Expand Up @@ -501,6 +509,13 @@ def validate_steward_team_plan_preview(
raise ValueError("steward team plan preview schema_version is invalid")
if plan.get("kind") != STEWARD_TEAM_PLAN_PREVIEW_KIND:
raise ValueError("steward team plan preview kind is invalid")
# The plan names the Goal it staffs. Without that, the admission that
# validates its lanes and the settlement that materializes them would each
# have to guess which Goal's agents the host should describe, and a plan
# could be admitted against one Goal's facts and applied under another's.
goal_id = _plan_text(plan.get("goal_id"), "goal_id")
if not _GOAL_ID.fullmatch(goal_id):
raise ValueError("steward team plan preview requires an exact Goal id")
registered = {str(value) for value in registered_agent_ids}
action_kinds = {str(value) for value in supported_action_kinds}
lanes_value = plan.get("lanes")
Expand Down Expand Up @@ -585,6 +600,7 @@ def validate_steward_team_plan_preview(
preview = {
"schema_version": STEWARD_TEAM_PLAN_PREVIEW_SCHEMA_VERSION,
"kind": STEWARD_TEAM_PLAN_PREVIEW_KIND,
"goal_id": goal_id,
"objective": _plan_text(plan.get("objective"), "objective"),
"lanes": lanes,
"gaps": gaps,
Expand Down
20 changes: 19 additions & 1 deletion tests/test_steward_team_plan_apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def _proposal(*, agent_id: str = AGENT_ID, extra_lane: dict | None = None) -> di
return {
"schema_version": "steward_team_plan_preview_v0",
"kind": "steward_team_plan_preview",
"goal_id": GOAL_ID,
"proposal_id": "proposal-team-plan",
"objective": "Stand up the intake lane",
"quota_envelope": {"slots_per_day": 4},
Expand Down Expand Up @@ -169,14 +170,16 @@ def test_a_gap_lane_creates_nothing_and_a_replay_adds_no_second_row(

def test_an_unknown_goal_is_refused_before_any_todo(tmp_path: Path) -> None:
project, registry_path = _fixture(tmp_path)
unknown = _proposal()
unknown["goal_id"] = "goal-that-does-not-exist"

with pytest.raises(ValueError, match="unknown Goal"):
settle_governed_transition_proposals(
registry_path=registry_path,
goal_id="goal-that-does-not-exist",
agent_id=AGENT_ID,
effect_id="effect-team-plan",
proposals=[_proposal()],
proposals=[unknown],
existing_receipts=[],
checkpoint=lambda _receipts: None,
phase=GovernedTransitionSettlementPhase.PRE_SETTLEMENT,
Expand All @@ -185,6 +188,21 @@ def test_an_unknown_goal_is_refused_before_any_todo(tmp_path: Path) -> None:
assert "loopx:todo " not in _todos(project)


def test_a_plan_cannot_be_retargeted_to_another_goal(tmp_path: Path) -> None:
"""Admission facts and the applied Goal have to be the same Goal."""

project, registry_path = _fixture(tmp_path)
retargeted = _proposal()
retargeted["goal_id"] = "some-other-goal"

with pytest.raises(ValueError, match="different Goal than its settlement"):
_settle(registry_path, retargeted)

# Nothing was created, and the named Goal's own plan still applies.
assert "loopx:todo " not in _todos(project)
assert _settle(registry_path, _proposal())[0]["action"] == "created"


def _second_lane() -> dict:
return {
"lane_id": "lane-beta",
Expand Down
19 changes: 19 additions & 0 deletions tests/test_steward_team_plan_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def _plan(**overrides: object) -> dict[str, object]:
plan: dict[str, object] = {
"schema_version": STEWARD_TEAM_PLAN_PREVIEW_SCHEMA_VERSION,
"kind": STEWARD_TEAM_PLAN_PREVIEW_KIND,
"goal_id": "team-plan-fixture",
"objective": "Ship the intake lane",
"quota_envelope": {"slots_per_day": 4},
"stop_condition": "Stop when the owner withdraws the request",
Expand Down Expand Up @@ -50,12 +51,30 @@ def test_a_staffed_lane_becomes_a_preview_that_cannot_apply() -> None:
preview = _validate(_plan())

assert preview["applies"] is False
assert preview["goal_id"] == "team-plan-fixture"
assert preview["gaps"] == []
assert preview["lanes"][0]["staffing"] == "ready"
assert preview["lanes"][0]["first_todo"]["priority"] == "P1"
assert preview["quota_envelope"] == {"slots_per_day": 4}


def test_the_preview_must_name_the_goal_it_staffs() -> None:
"""Admission and settlement both need one named Goal's facts."""

without_goal = _plan()
del without_goal["goal_id"]
with pytest.raises(ValueError, match="goal_id must be a non-empty string"):
_validate(without_goal)

# A Goal id is an exact registry id, not free text: a path, a sentence or an
# unbounded string cannot become the Goal a settlement materializes into.
for invalid in ("", " ", "../escape", "goal with spaces", "x" * 161):
with pytest.raises(ValueError):
_validate(_plan(goal_id=invalid))
with pytest.raises(ValueError, match="requires an exact Goal id"):
_validate(_plan(goal_id="../escape"))


def test_an_unregistered_agent_becomes_a_gap_instead_of_being_invented() -> None:
plan = _plan()
plan["lanes"][0]["agent_id"] = "agent-not-registered" # type: ignore[index]
Expand Down
Loading