Skip to content

Commit 72b0fdc

Browse files
committed
Merge remote-tracking branch 'origin/main' into codex/pr5676-local-workspace-repair
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
2 parents 615324e + 23935d5 commit 72b0fdc

6 files changed

Lines changed: 249 additions & 6 deletions

File tree

‎docs/architecture/rfcs/loopx-overall-roadmap-v0.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,16 @@ is general semantic triage, not a new steward-specific classifier or scheduler.
814814
Typed admission owns provider-independent trigger reasons; the App renders those
815815
facts without treating a configuration change as retroactive execution.
816816

817+
Native steward private admission now supplies the existing inbox's exact
818+
provider provenance before Turn launch, including prepared-request replay.
819+
It follows the resolved coordination owner when Chat storage is separate, with
820+
the retained layout for legacy controllers; source evidence does not fork into
821+
the private Chat store.
822+
Operator read/delivery configuration recognizes its full App/source channel;
823+
portfolio read alone still grants no delivery. Source/policy/replay fixtures
824+
qualify this entry prerequisite, not worker launch, adoption, automatic wake or
825+
original-route completion. Those remain the same R3 acceptance gates above.
826+
817827
### R4: Shared Goal Alignment and Evolution
818828

819829
- **Owner:** alignment RFC Stage 3–5 and TS Goal/work-graph owners.

‎loopx/capabilities/manager_context/README.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ external delivery, including future replay. Existing inbox records are retained.
5353
Provider ingress receipts bind message digest, channel and sender; a model cannot
5454
create or widen that provenance through its response.
5555

56+
Native steward private conversations record that provenance before the canonical
57+
Turn can start, using this App's independently verified opaque `operator_ref`.
58+
Use the exact Session channel `manager.external.native.<binding-ref>.<source-ref>`
59+
and that owner reference in the private source policy. The same read-scope and
60+
delivery-target commands accept this exact channel alongside legacy channels;
61+
partial channels and wildcards are rejected. Native admission and crash replay
62+
retain the original Session, request and message. An App's portfolio read grant
63+
alone does not grant context delivery, and inbox delivery does not launch a worker.
64+
Provenance uses the controller's resolved coordination runtime root when present,
65+
so a separate Chat store cannot split it from the recipient policy and inbox.
66+
Controllers predating that root retain the existing Chat-parent layout.
67+
Messages outside the shared inbox's source-context bounds remain intact in
68+
ordinary Chat, but cannot be handed off as inline context; use a scoped artifact
69+
for larger material. Image content is not transferred by this text ingress record.
70+
5671
The existing worker turn-start hook exposes only a bounded pending count and
5772
required read command, without copying private content into status projections.
5873
Read and record a decision through the installed CLI:

‎loopx/capabilities/manager_context/__init__.py‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,11 +324,17 @@ def evidence_goal_scope(runtime_root: Path, channel: str) -> list[str] | None:
324324
return []
325325

326326

327+
def _require_external_channel(channel: str) -> None:
328+
# Legacy manager connections and native App/source bindings both identify
329+
# one exact audience. Never accept a prefix or a partially specified binding.
330+
if not re.fullmatch(r"manager\.external\.(?:[a-f0-9]{24}|native\.[a-f0-9]{24}\.[a-f0-9]{24})", channel):
331+
raise ValueError("an exact external manager channel is required")
332+
333+
327334
def configure_evidence_scope(runtime_root: Path, registry_path: Path, *, channel: str,
328335
goal_ids: list[str], execute: bool = False) -> dict:
329336
"""Local operator grants only selected Goal summaries to an exact audience."""
330-
if not re.fullmatch(r"manager\.external\.[a-f0-9]{24}", channel):
331-
raise ValueError("an exact external manager channel is required")
337+
_require_external_channel(channel)
332338
registry = load_project_registry(registry_path)
333339
available = {g.get("id") for g in registry.get("goals", []) if isinstance(g, dict)}
334340
if any(g not in available for g in goal_ids):
@@ -361,8 +367,7 @@ def configure_delivery_target(
361367
execute: bool = False,
362368
) -> dict:
363369
"""Observe registry/policy and persist a typed sender-bound recipient change."""
364-
if not re.fullmatch(r"manager\.external\.[a-f0-9]{24}", channel):
365-
raise ValueError("an exact external manager channel is required")
370+
_require_external_channel(channel)
366371
path = _root(runtime_root) / "policy.json"
367372

368373
def update(*, apply: bool) -> dict:

‎loopx/capabilities/native_chat/external_conversations.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def _admit_prepared(self, path: Path, row: dict[str, Any], selected: dict[str, A
9494
if controller.store.turn_for_client(current["session_id"], client_id) is not None:
9595
# The canonical store validates exact replay before its closed
9696
# Session check. Never move an accepted request to a new Session.
97+
self._record_steward_ingress(row, selected, current, client_id)
9798
turn, _ = controller.store.create_queued_turn(current["session_id"],
9899
client_turn_id=client_id, message=row["message"], origin="lark",
99100
external_agent_target={"target": target, "context": selected["context"]} if target else None)
@@ -176,6 +177,7 @@ def _admit_prepared(self, path: Path, row: dict[str, Any], selected: dict[str, A
176177
mode="resume_latest", conversation_binding_id=row["binding_id"], source_context=row["source"])
177178
row["session_id"] = current["session_id"]
178179
_atomic_write_json(path, row)
180+
self._record_steward_ingress(row, selected, current, plan["client_turn_id"])
179181
try:
180182
turn, _ = controller.enqueue_turn(session_id=current["session_id"],
181183
client_turn_id=plan["client_turn_id"], message=row["message"],
@@ -189,6 +191,32 @@ def _admit_prepared(self, path: Path, row: dict[str, Any], selected: dict[str, A
189191
_atomic_write_json(path, row)
190192
return row
191193

194+
def _record_steward_ingress(self, row: dict[str, Any], selected: dict[str, Any],
195+
session: dict[str, Any], client_turn_id: str) -> None:
196+
if selected["binding"]["context_kind"] != "steward":
197+
return
198+
if session.get("goal_id") != "loopx-manager" or session.get("channel_id") != selected["channel_id"]:
199+
raise ValueError("the verified steward source belongs to another Session")
200+
from ..manager_context import register_ingress
201+
from ...control_plane.collaboration.inbox import normalize_source_context
202+
203+
try:
204+
normalize_source_context(row["message"])
205+
except ValueError:
206+
# Inbox bounds are not ordinary Chat admission bounds. Preserve the
207+
# full native message; no provenance means no context delivery.
208+
return
209+
210+
# Persist provenance before enqueue can launch the model. This is the
211+
# existing inbox owner, not a delivery grant: its separate sender/target
212+
# policy still decides which registered recipient may receive context.
213+
# The independently verified App-scoped owner reference is deliberately
214+
# used instead of copying another profile's raw provider identity.
215+
register_ingress(getattr(self.controller, "coordination_runtime_root", self.controller.store.root.parent),
216+
session_id=session["session_id"], client_turn_id=client_turn_id,
217+
channel=selected["channel_id"], sender_id=selected["context"]["operator_ref"],
218+
message=row["message"], source_id=row["request_ref"])
219+
192220
def pending(self) -> list[dict[str, Any]]:
193221
return [_read_json(path) for path in sorted(self.root.glob("*.json"))]
194222

‎loopx/semantics/project_registry_io_manifest_v1.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,15 +231,15 @@
231231
},
232232
{
233233
"site": "loopx/capabilities/manager_context/__init__.py::<module>.configure_delivery_target.update::codec_read:load_project_registry#1",
234-
"line": 377,
234+
"line": 382,
235235
"column": 51,
236236
"kind": "codec_read",
237237
"api": "load_project_registry",
238238
"classification": "codec_api"
239239
},
240240
{
241241
"site": "loopx/capabilities/manager_context/__init__.py::<module>.configure_evidence_scope::codec_read:load_project_registry#1",
242-
"line": 332,
242+
"line": 338,
243243
"column": 16,
244244
"kind": "codec_read",
245245
"api": "load_project_registry",
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
"""Verified native sources can reach the existing, separately granted inbox."""
2+
import json
3+
4+
import pytest
5+
6+
from test_native_steward_private import steward, finish # noqa: F401
7+
from loopx.capabilities.manager_context import (
8+
POLICY_SCHEMA, _hash, _root, _write, authority, configure_delivery_target,
9+
configure_evidence_scope, deliver, pending,
10+
)
11+
12+
13+
def ingress_path(store, row):
14+
return _root(store.root.parent) / "ingress" / (
15+
_hash([row["session_id"], "external-" + row["request_ref"]]) + ".json")
16+
17+
18+
@pytest.mark.parametrize("separate_coordination_root", [False, True])
19+
def test_native_ingress_uses_coordination_owner_with_legacy_fallback(steward, monkeypatch, tmp_path, separate_coordination_root): # noqa: F811
20+
store, runtime, provider, transport, binding, _, workspace = steward
21+
root = tmp_path / "coordination" if separate_coordination_root else store.root.parent
22+
if separate_coordination_root:
23+
runtime.coordination_runtime_root = root
24+
target = {"goal_id": "registered-work", "agent_id": "worker"}
25+
registry = json.loads(runtime.registry_path.read_text())
26+
registry["runtime_root"] = str(root)
27+
registry["goals"] = [{"id": target["goal_id"], "repo": str(workspace),
28+
"coordination": {"registered_agents": [target["agent_id"]]}}]
29+
runtime.registry_path.write_text(json.dumps(registry))
30+
# Observe provenance at the actual enqueue boundary, before model execution.
31+
monkeypatch.setattr(runtime, "resume_session_queue", lambda **kwargs: None)
32+
enqueue = runtime.enqueue_turn
33+
saved = []
34+
35+
def observe(**kwargs):
36+
path = _root(root) / "ingress" / (_hash([kwargs["session_id"], kwargs["client_turn_id"]]) + ".json")
37+
saved.append(path.read_bytes())
38+
return enqueue(**kwargs)
39+
40+
monkeypatch.setattr(runtime, "enqueue_turn", observe)
41+
event = provider.event("steward-app", "canonical", "Original request for the registered worker")
42+
assert transport.admit("steward-app", event)["status"] == "durably_accepted"
43+
row = transport.core.pending()[0]
44+
session = store.load_session(row["session_id"])
45+
turn = store.load_turn(row["session_id"], row["turn_id"])
46+
assert turn["status"] == "queued" and len(saved) == 1
47+
if separate_coordination_root:
48+
assert not ingress_path(store, row).exists()
49+
policy_path = _root(root) / "policy.json"
50+
_write(policy_path, {"schema_version": POLICY_SCHEMA, "sources": {session["channel_id"]: {
51+
"sender_ids": [binding["operator_ref"]], "local_delivery_scope": "selected", "targets": [],
52+
}}})
53+
assert authority(root, runtime.registry_path, session, turn)["targets"] == []
54+
configure_evidence_scope(root, runtime.registry_path, channel=session["channel_id"],
55+
goal_ids=[target["goal_id"]], execute=True)
56+
configure_delivery_target(root, runtime.registry_path, channel=session["channel_id"],
57+
**target, grant=True, execute=True)
58+
receipt = deliver(root, runtime.registry_path, session=session, turn=turn, request=target)
59+
assert pending(root, **target)["items"][0]["message"] == event["content"]
60+
assert deliver(root, runtime.registry_path, session=session, turn=turn,
61+
request=target) == {**receipt, "replayed": True}
62+
assert transport.admit("steward-app", event)["status"] == "durably_accepted"
63+
assert len(saved) == 1 and len(store.list_sessions()) == 1
64+
# A sibling App's project source cannot acquire manager provenance or policy.
65+
monkeypatch.setattr(runtime, "enqueue_turn", enqueue)
66+
transport.admit("notes-app", provider.event("notes-app", "other", "Other audience"))
67+
notes = next(item for item in transport.core.pending() if item["message"] == "Other audience")
68+
assert not authority(root, runtime.registry_path, store.load_session(notes["session_id"]),
69+
store.load_turn(notes["session_id"], notes["turn_id"]))["targets"]
70+
configure_delivery_target(root, runtime.registry_path, channel=session["channel_id"],
71+
**target, grant=False, execute=True)
72+
with pytest.raises(ValueError, match="not authorized"):
73+
deliver(root, runtime.registry_path, session=session, turn=turn, request=target)
74+
75+
76+
def test_verified_ingress_precedes_model_launch_and_survives_admission_replay(steward, monkeypatch): # noqa: F811
77+
store, runtime, provider, transport, binding, _, _ = steward
78+
enqueue = runtime.enqueue_turn
79+
observations = []
80+
81+
def interrupted(**kwargs):
82+
row = transport.core.pending()[0]
83+
saved = json.loads(ingress_path(store, row).read_text())
84+
assert saved["client_turn_id"] == kwargs["client_turn_id"]
85+
assert saved["sender_id"] == binding["operator_ref"]
86+
assert saved["source_id"] == row["request_ref"]
87+
assert saved["message_digest"] == _hash(kwargs["message"])
88+
observations.append(saved)
89+
enqueue(**kwargs)
90+
raise OSError("owner interrupted after canonical admission")
91+
92+
event = provider.event("steward-app", "first", "Original owner context")
93+
monkeypatch.setattr(runtime, "enqueue_turn", interrupted)
94+
with pytest.raises(OSError):
95+
transport.admit("steward-app", event)
96+
prepared = transport.core.pending()[0]
97+
before = ingress_path(store, prepared).read_bytes()
98+
turn = store.turn_for_client(prepared["session_id"], "external-" + prepared["request_ref"])
99+
assert finish(runtime, {**prepared, "turn_id": turn["turn_id"]})["status"] == "completed"
100+
monkeypatch.setattr(runtime, "enqueue_turn", enqueue)
101+
assert transport.admit("steward-app", event)["status"] == "durably_accepted"
102+
accepted = transport.core.pending()[0]
103+
assert accepted["session_id"] == prepared["session_id"] and accepted["turn_id"] == turn["turn_id"]
104+
assert ingress_path(store, accepted).read_bytes() == before and len(observations) == 1
105+
assert len(store.list_sessions()) == 1
106+
# A regular project source cannot create manager provenance or a Goal.
107+
transport.admit("notes-app", provider.event("notes-app", "notes", "Private project context"))
108+
notes = next(row for row in transport.core.pending() if row["message"] == "Private project context")
109+
assert finish(runtime, notes)["status"] == "completed"
110+
assert not ingress_path(store, notes).exists()
111+
112+
113+
def test_native_steward_uses_separate_sender_target_grants_and_exact_return_route(steward): # noqa: F811
114+
store, runtime, provider, transport, binding, _, workspace = steward
115+
target = {"goal_id": "fresh-commission", "agent_id": "operations"}
116+
registry = json.loads(runtime.registry_path.read_text())
117+
registry["goals"] = [{"id": target["goal_id"], "repo": str(workspace),
118+
"coordination": {"registered_agents": [target["agent_id"]]}}]
119+
runtime.registry_path.write_text(json.dumps(registry))
120+
registry_before = runtime.registry_path.read_bytes()
121+
event = provider.event("steward-app", "source", "Read this source; return private findings, never publish.")
122+
assert transport.admit("steward-app", event)["status"] == "durably_accepted"
123+
row = transport.core.pending()[0]
124+
assert finish(runtime, row)["status"] == "completed"
125+
session = store.load_session(row["session_id"])
126+
turn = store.load_turn(row["session_id"], row["turn_id"])
127+
root = store.root.parent
128+
assert authority(root, runtime.registry_path, session, turn)["targets"] == []
129+
policy = {"schema_version": POLICY_SCHEMA, "sources": {session["channel_id"]: {
130+
"sender_ids": [binding["operator_ref"]], "local_delivery_scope": "selected", "targets": [],
131+
}}}
132+
policy_path = _root(root) / "policy.json"
133+
_write(policy_path, policy)
134+
# Reading a portfolio does not confer delivery authority.
135+
configure_evidence_scope(root, runtime.registry_path, channel=session["channel_id"],
136+
goal_ids=[target["goal_id"]], execute=True)
137+
assert authority(root, runtime.registry_path, session, turn)["targets"] == []
138+
preview = configure_delivery_target(root, runtime.registry_path, channel=session["channel_id"], **target, grant=True)
139+
assert preview["would_change"] and not preview["executed"]
140+
assert authority(root, runtime.registry_path, session, turn)["targets"] == []
141+
applied = configure_delivery_target(root, runtime.registry_path, channel=session["channel_id"], **target, grant=True, execute=True)
142+
assert applied["readback_verified"]
143+
assert authority(root, runtime.registry_path, session, turn)["targets"] == [target]
144+
assert not authority(root, runtime.registry_path, session, {**turn, "message": "forged"})["targets"]
145+
assert not authority(root, runtime.registry_path, session, {**turn, "origin": "web"})["targets"]
146+
receipt = deliver(root, runtime.registry_path, session=session, turn=turn, request=target)
147+
assert not receipt["todo_created"] and not receipt["execution_interrupted"]
148+
assert deliver(root, runtime.registry_path, session=session, turn=turn, request=target) == {**receipt, "replayed": True}
149+
assert pending(root, **target)["items"][0]["message"] == event["content"]
150+
assert runtime.registry_path.read_bytes() == registry_before
151+
assert policy_path.stat().st_mode & 0o777 == 0o600
152+
# The independently verified second App cannot use this channel's grant.
153+
transport.admit("notes-app", provider.event("notes-app", "notes", "Other private audience"))
154+
notes = next(row for row in transport.core.pending() if row["message"] == "Other private audience")
155+
assert finish(runtime, notes)["status"] == "completed"
156+
assert not authority(root, runtime.registry_path, store.load_session(notes["session_id"]),
157+
store.load_turn(notes["session_id"], notes["turn_id"]))["targets"]
158+
policy["sources"][session["channel_id"]]["targets"] = [target]
159+
policy["sources"][session["channel_id"]]["sender_ids"] = ["another-App-owner"]
160+
_write(policy_path, policy)
161+
with pytest.raises(ValueError, match="not authorized"):
162+
deliver(root, runtime.registry_path, session=session, turn=turn, request=target)
163+
164+
165+
@pytest.mark.parametrize("channel", ["manager.external.native", "manager.external.native." + "a" * 24,
166+
"manager.external.native." + "a" * 24 + ".*",
167+
"manager.external." + "a" * 23])
168+
def test_configuration_rejects_partial_or_wildcard_audiences(tmp_path, channel):
169+
with pytest.raises(ValueError, match="exact external"):
170+
configure_evidence_scope(tmp_path, tmp_path / "missing.json", channel=channel, goal_ids=[])
171+
with pytest.raises(ValueError, match="exact external"):
172+
configure_delivery_target(tmp_path, tmp_path / "missing.json", channel=channel,
173+
goal_id="fresh-commission", agent_id="operations", grant=True)
174+
175+
176+
def test_inbox_bound_does_not_truncate_or_reject_ordinary_native_chat(steward, monkeypatch): # noqa: F811
177+
store, runtime, provider, transport, _, _, _ = steward
178+
monkeypatch.setattr(runtime, "resume_session_queue", lambda **kwargs: None)
179+
message = "x" * 32001
180+
assert transport.admit("steward-app", provider.event("steward-app", "large", message))["status"] == "durably_accepted"
181+
row = transport.core.pending()[0]
182+
turn = store.load_turn(row["session_id"], row["turn_id"])
183+
assert turn["message"] == message and turn["status"] == "queued"
184+
assert not ingress_path(store, row).exists()
185+
assert not authority(store.root.parent, runtime.registry_path, store.load_session(row["session_id"]), turn)["targets"]

0 commit comments

Comments
 (0)