Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- Supersedes / closes: none
- Proposed by: LoopX maintainers
- Date: 2026-08-15
- Last revised: 2026-10-20
- Last revised: 2026-10-21
- Scope: an incremental, replacement-first migration of the LoopX control-plane
core from Python to TypeScript without maintaining two semantic
implementations
Expand Down Expand Up @@ -36,6 +36,14 @@ delivery. Fewer requests alone do not establish a performance gain.

### Product conversations and asynchronous inbox

**Bounded Goal portfolio reads.** `goal_portfolio.py` now joins the existing
executable-revision request scope around the complete portfolio read. CLI,
Chat context and manager inspection callers reuse that scope across status and
per-Agent quota reads. Nested scopes join it; the next portfolio read resolves
the current executable again. Registry, provider and run-history reads remain
live, with their existing scope, limits and change detection. This is bounded
caller adoption: no new bridge, data cache, semantic owner or provider routing.

**Receiver Inbox read transaction (2026-10-19).**
`loopx/control_plane/collaboration/inbox_read.ts` now composes local source scans, pagination,
Goal-instance admission, receiver and peer-return read receipts, and unfinished
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- 替代 / 关闭:无
- Proposed by:LoopX maintainers
- Date:2026-08-15
- Last revised:2026-10-20
- Last revised:2026-10-21
- Scope:LoopX 控制面核心从 Python 到 TypeScript 的增量、replacement-first
迁移;不长期维护两份语义实现
- Tracking issue:[#3225](https://github.com/loopx-project/loopx/issues/3225)
Expand Down Expand Up @@ -34,6 +34,13 @@ SQLite CLI 性能。仅减少请求次数不能证明性能收益。

### 产品对话与通用异步 inbox

**有界 Goal portfolio 读取。** `goal_portfolio.py` 现在在完整 portfolio 读取期间
复用既有 executable-revision 请求作用域。CLI、Chat context 和 manager inspection
调用者在 status 与逐 Agent quota 读取之间共用该作用域。嵌套作用域加入当前请求;
下一次 portfolio 读取重新解析当前代码。Registry、provider 与 run-history 仍实时
读取,保留既有范围、上限和变化检查。这是有界调用者接入,不新增桥接、数据缓存、
语义 owner 或 provider 路由。

**接收方 Inbox 读取事务(2026-10-19)。**
`loopx/control_plane/collaboration/inbox_read.ts` 现在通过既有 typed owner 组合本地源扫描、分页、
Goal-instance 准入、接收方及 peer-return 读取回执和未完成委派上下文。
Expand Down
2 changes: 2 additions & 0 deletions loopx/goal_portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from typing import Any

from .agent_registry import registered_agent_ids_for_goal
from .control_plane.effect_runtime import effect_runtime_request_scope
from .control_plane.goals.activation import goal_activation_state
from .control_plane.goals.goal_ref_validation import exact_goal_ref
from .control_plane.quota.should_run import build_quota_should_run
Expand Down Expand Up @@ -327,6 +328,7 @@ def _read_goal(
return row


@effect_runtime_request_scope()
def build_goal_portfolio(
*,
registry_path: Path,
Expand Down
39 changes: 39 additions & 0 deletions tests/test_goal_portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,3 +516,42 @@ def read(goal, **kwargs):
assert result['coverage']['stopped_excluded'] == 0
assert result['goals'][0]['activation_state'] == 'unknown'
assert result['goals'][0]['quality'] == 'conflicting'


@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_portfolio_resolves_code_once_per_read_and_keeps_registration_live(
tmp_path, monkeypatch, provider,
):
from tests.control_plane.canonical_authority_fixture import (
isolate_sqlite_runtime, promoted_create_fixture,
)
from loopx.control_plane import effect_runtime

isolate_sqlite_runtime(tmp_path, monkeypatch)
path, _, state = promoted_create_fixture(
tmp_path, provider=provider, registered_agents=["agent-a", "agent-b"],
)
source = json.loads(path.read_text())
source["goals"][0].update(domain="portfolio", adapter={"kind": "generic_project_goal_v0"})
path.write_text(json.dumps(source))
state_before = state.read_bytes()
fingerprints = []
fingerprint = effect_runtime._runtime_fingerprint

def observe_code_read():
revision = fingerprint()
fingerprints.append(revision)
return revision

monkeypatch.setattr(effect_runtime, "_runtime_fingerprint", observe_code_read)
first = portfolio.build_goal_portfolio(registry_path=path)
assert len(fingerprints) == 1, f"resolved code {len(fingerprints)} times"
assert [a["agent_id"] for a in first["goals"][0]["agents"]] == ["agent-a", "agent-b"]
source["goals"][0]["coordination"]["registered_agents"] = ["agent-b"]
path.write_text(json.dumps(source))
second = portfolio.build_goal_portfolio(registry_path=path)
assert len(fingerprints) == 2
assert [a["agent_id"] for a in second["goals"][0]["agents"]] == ["agent-b"]
assert second["inventory_revision"] != first["inventory_revision"]
assert second["goals"][0]["progress"] == "unknown"
assert state.read_bytes() == state_before
Loading