Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b0e7bfb
fix(qualification): align model recovery and result instructions
loopx-agent Nov 8, 2026
7c18d78
Preserve bounded recovery after isolated draft shell errors
loopx-agent Nov 8, 2026
8a9bbb7
Merge checkpoint fixture repair into the 2.3.0 qualification candidate
loopx-agent Nov 8, 2026
e9856eb
chore(quality): bound reviewed Lark reply context growth
loopx-agent Nov 8, 2026
695cc49
Integrate reviewed release prerequisites from main
loopx-agent Nov 8, 2026
553733d
Integrate the measured Lark module ceiling for release qualification
loopx-agent Nov 8, 2026
245cb59
test(quality): retain terminal correction room with complete context …
loopx-agent Nov 8, 2026
e4b2fff
Integrate bounded 2.3.0 qualification recovery on current main
loopx-agent Nov 8, 2026
8106fc7
Preserve correctable missing-draft feedback within the terminal tool …
loopx-agent Nov 8, 2026
c207ba3
Keep selected context values exact without inflating short scalar arrays
loopx-agent Nov 8, 2026
d71cb1c
Keep bounded reader and refresh settlement adapters with their existi…
loopx-agent Nov 8, 2026
244a269
Validate native fixture recovery and retain complete monitor closeout…
loopx-agent Nov 8, 2026
3f1cb51
Complete bilingual 2.3.0 release decisions, optional usage and commun…
loopx-agent Nov 8, 2026
0bab447
Integrate qualified release recovery with current complete-source Tur…
loopx-agent Nov 8, 2026
27ca9ba
Preserve valid root object projections in captured replan readback
loopx-agent Nov 8, 2026
34af6a5
Resolve shared-topic ambiguity and return available accepted report c…
loopx-agent Nov 8, 2026
e71bf9c
Keep capture selection navigation in its bounded reader owner
loopx-agent Nov 8, 2026
4055e74
Complete bilingual release recovery notes and historical attribution
loopx-agent Nov 8, 2026
9093899
Merge commit '4055e743c9d335f5ad19a1b358a90532938f22dc' into codex/re…
loopx-agent Nov 8, 2026
2941018
fix(release): deliver current task operations in native first reads
loopx-agent Nov 8, 2026
c108b2c
test(release): bind paged witnesses to the requested source
loopx-agent Nov 8, 2026
7e00054
Merge remote-tracking branch 'refs/remotes/origin/release-v230-blocke…
loopx-agent Nov 8, 2026
c32effa
fix(context): keep saved replan reads on the verified capture
loopx-agent Nov 8, 2026
1ac6cc5
fix(dsh): consume raw quota admission in the continuation driver
loopx-agent Nov 8, 2026
3ea294b
docs(release): cover DSH workspace activation and merged contributions
loopx-agent Nov 8, 2026
5efb4b9
fix(quota): clarify evidence-linked Todo settlement
loopx-agent Nov 8, 2026
aabb5b9
fix(semantics): register the shared admitted route producer
loopx-agent Nov 8, 2026
cd60523
fix(review): include the appointed responder in SLA reporting
loopx-agent Nov 8, 2026
22236f2
docs(release): preserve mainline benchmark retry guidance
loopx-agent Nov 8, 2026
14e709e
test(release): exercise current admission and readback contracts
loopx-agent Nov 8, 2026
63ec18d
Merge verified main into the LoopX 2.3.0 candidate
loopx-agent Nov 8, 2026
3e6bf3e
docs(release): cover integrated receiver and review workflows
loopx-agent Nov 8, 2026
a58e516
test(architecture): pin module budget after Chat status move
loopx-agent Nov 8, 2026
bdef8a5
test(quota): verify current semantic closeout instructions
loopx-agent Nov 8, 2026
fb14386
Merge staged Stride review guidance into the 2.3.0 candidate
loopx-agent Nov 8, 2026
c1c3a95
docs(release): show staged Stride review readback
loopx-agent Nov 8, 2026
83495d3
fix(validation): keep public links and peer boundary checks current
loopx-agent Nov 8, 2026
43a82cc
test(release): exercise current host accounting and reply attribution
loopx-agent Nov 8, 2026
bdb06bc
Merge prepared quota transport into the 2.3.0 candidate
loopx-agent Nov 8, 2026
d855b2c
fix(chat): keep the review envelope example at the end of the prompt
loopx-agent Nov 8, 2026
be3dbef
docs(release): verify saved selections and original-source Turn recovery
loopx-agent Nov 8, 2026
91d59d5
Clarify evidence requirements before terminal Todo settlement
loopx-agent Nov 8, 2026
c6c61d1
Keep Vision evidence guidance within its CLI budget
loopx-agent Nov 8, 2026
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
3 changes: 2 additions & 1 deletion apps/usage-collector/test/collector.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ for (const version of ["1.2.4", "2.0.0a1", "2.0.0b2", "2.0.0rc3"]) {
await new Promise(resolve => server.listen(0, "127.0.0.1", resolve));
const root = await mkdtemp(join(tmpdir(), "loopx-real-collector-")), path = join(root, "usage.json");
t.after(async () => { server.closeAllConnections(); server.close(); await rm(root, { recursive: true, force: true }); });
const ctx = { env: { LOOPX_USAGE_PING_ENDPOINT: "http://127.0.0.1:" + server.address().port + "/v1/ping" },
const ctx = { env: { LOOPX_USAGE_PING: "1",
LOOPX_USAGE_PING_ENDPOINT: "http://127.0.0.1:" + server.address().port + "/v1/ping" },
version, python: "3.13", channel: "source", now };
await configureContext(path, ctx, "personal"); await configure(path, ctx, "enable");
const generation = JSON.parse(await readFile(path, "utf8")).generation;
Expand Down
2 changes: 1 addition & 1 deletion docs/development/bugs/workspace-status-access-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
→ `collect_status` → 状态依赖读取或 Effect runtime → 错误响应 → 逐 Goal 提示。

归属仍是现有 Dashboard 状态读取与展示边界:
[chat_status_api.py](../../../loopx/chat_status_api.py)、
[chat/status_api.py](../../../loopx/chat/status_api.py)、
[workspace-progressive-status.ts](../../../apps/presentation/dashboard/src/data/workspace-progressive-status.ts)
和 [i18n.tsx](../../../apps/presentation/dashboard/src/features/personal-workspace/i18n.tsx)。
复用 [Effect runtime](../../../loopx/control_plane/effect_runtime.py) 的类型及诊断码;
Expand Down
25 changes: 25 additions & 0 deletions docs/development/testing-and-quality.md
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,31 @@ Heartbeat 的正文回归计量先归一化已选择的 runtime、registry 和 a
or promotion thresholds stay fixed for that result; revised thresholds belong
to a new qualification, never a relabeled historical pass.

The auxiliary-monitor recovery fixture measures the complete original
writeback step with indented JSON, using fixed-width disposable routes on
both File and SQLite. The release base emits 8,642 characters and fails the
former 7,500-character regression ceiling; that failure remains recorded.
The current step carries the complete Vision field and path-decision bounds,
evidence-linked continuation/cost review, original identity and commands.
Deleting those obligations or splitting this small recovery step into another
reference would change its consumer contract. Calibrate only this regression
ceiling to 9,336, retaining the former 694 characters of headroom. Compare
complete authoring bounds and parsed semantic-closeout arguments with the
original admitted contract; recovery specializes the completed Task's boundary.
Spend and settled
recovery keep their 2,000-character ceilings. This preserves useful guidance;
it is neither an output reduction nor a change to any external host limit or
frozen performance criterion. Candidate measurements must still pass.

辅助 Monitor 的恢复夹具用缩进 JSON 测量完整的原始 writeback 步骤,File 与 SQLite
均使用固定宽度的临时路由。发布基线均为 8,642 字符,原 7,500 字符回归上限的失败
继续保留。当前步骤包含完整 Vision 字段与路径决策约束、带证据的继续执行及成本
判断、原始身份和命令。删除这些义务或给这段小型恢复指令另加引用都会改变消费
合同,因此只将该回归上限校准为 9,336,保留原 694 字符余量,并与最初准入合同
逐字段比较完整 authoring 约束与解析后的 semantic-closeout 命令参数;恢复步骤保留
已完成 Task 所需的具体结算边界。spend 和 settled 恢复仍受 2,000 字符上限约束。这不代表输出
缩小,不改变外部宿主硬限制或冻结性能标准;候选版仍须重新测量并通过检查。

Native progressive-context qualification found an existing default-output
budget failure: immutable base `fb331f45c` and candidate `edbd56b14` both emit
25,836 JSON characters for `quota_should_run/multi_agent` with the unchanged
Expand Down
344 changes: 258 additions & 86 deletions docs/product/releases/v2.3.0.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,9 @@ Status, unfiltered `todo list`, `todo list --thin` without an exact ID,
dashboard and chat attention views keep their existing bounded summaries.
An exact `todo list --todo-id ID` without `--thin` returns the complete current
or retained request text through the existing Task detail reader. The canonical
provider and regenerated active state retain the complete source; a display
summary is not an input to source serialization. Combining `--thin` with an
provider and the File Markdown projection retain the complete source. SQLite
renders a bounded active view and includes an exact Todo query for any omitted
fields. Combining `--thin` with an
exact Todo ID is rejected so a bounded inventory projection cannot replace a
full requirement read. No new frontend setting, Lark command or parallel state
store is introduced. This change stays in the permanent Python Markdown/legacy
Expand All @@ -256,8 +257,9 @@ their existing closed diagnostics.
上限。显式标题冲突仍拒绝,即使前 500 字符相同;不能靠缩短已提交任务来修显示。
状态、未过滤的 `todo list`、未指定精确 ID 的 `todo list --thin`、前端及聊天继续使用原有
有界摘要。`todo list --todo-id ID` 返回完整的当前或保留请求文本;同时指定 `--thin` 时会
拒绝请求,避免有界摘要替代完整需求读取。canonical provider 及重建后的活动状态保留完整
源文本,不新增设置、Lark 命令或第二状态库。本次仅修既有 Python Markdown/兼容 I/O,TS
拒绝请求,避免有界摘要替代完整需求读取。canonical provider 和 File Markdown 投影保留完整
源文本。SQLite 渲染有界活动视图,为被省略的字段附上精确 Todo 查询。不新增设置、Lark 命令
或第二状态库。本次仅修既有 Python Markdown/兼容 I/O,TS
的权威、准入和交付确认 owner 不变。

若业务已提交、长文本一致性失败导致显示 pending,先读取当前 provider revision,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ def hierarchy_scan_line(path: Path, line: str) -> str:
if (path == REPO_ROOT / "docs/architecture/rfcs/controller-executor-unified-contract-v0.md"
and line.strip() == 'the current single Agent-scoped slot. One controller owns control dialogue;'):
return line.replace("controller owns", "native session role owns")
# Benchmark recovery assigns retries to the experiment provider or controller.
if (path == REPO_ROOT / "loopx/capabilities/benchmark_toolkit/README.md"
and line.strip() == "grading alive until it drains. The provider/controller owns bounded retry after"):
return line.replace("provider/controller owns", "benchmark provider or controller handles")
return line


Expand All @@ -79,6 +83,7 @@ def main() -> int:
"handoff_agent",
"agent_profile_v0",
"primary_checkout",
"controller owns",
):
assert LEGACY_PATTERN.search(token), token
for domain_term in ("primary_reviewers", "primary_review_score"):
Expand Down
1 change: 1 addition & 0 deletions examples/evaluations/chat-intake.public.json
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@
"status": "accepted",
"covers": "Microsoft cash flow FY2023–2025 with annual-report citations",
"content_read": true,
"content": "# report-v2 — Microsoft cash-flow comparison, FY2023–2025\n\nPublic synthetic test artifact; the following index units are fixture data, not Microsoft's actual financial figures.\n\n| Fixture year | Operating cash flow | Capital expenditure | Difference |\n| --- | ---: | ---: | ---: |\n| FY2023 | 100 | 40 | 60 |\n| FY2024 | 110 | 50 | 60 |\n| FY2025 | 120 | 60 | 60 |\n\nIn this synthetic comparison, both series rise and operating cash flow exceeds capital expenditure in every year. Sources in this fixture: annual-report-FY2023, annual-report-FY2024, annual-report-FY2025. This is the complete accepted fixture report, ready to return with its report-v2 reference.",
"observed_at": "2026-09-01T12:05:00Z"
}
],
Expand Down
10 changes: 7 additions & 3 deletions examples/personal-workspace-browser/research-history.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -866,7 +866,7 @@ export const researchHistoryScenario = {
if (response.status() === 409) return route.fulfill({response});
assert.equal(response.status(), 200);
const value = await response.json();
if (new URL(route.request().url()).searchParams.get("view") === "history") pages.push(value);
if (query.get("view") === "history") pages.push({goalId: query.get("goal_id"), packet: value});
await route.fulfill({response, json: value});
});
await page.route("**/api/chat/todo/detail?*", async route => {
Expand All @@ -889,7 +889,8 @@ export const researchHistoryScenario = {
const history = page.getByRole("region", {name: "研究历程", exact: true});
await history.getByRole("status").filter({hasText: "76 / 76"}).waitFor();
assert.equal(pages.length, 2, "The complete Portfolio checkpoint requires every bounded HTTP page");
assert.deepEqual(pages.flatMap(value => value.items).map(row => row.event_id), capture.events.map(row => row.event_id));
assert.deepEqual(pages.filter(value => value.goalId === capture.goal_id)
.flatMap(value => value.packet.items).map(row => row.event_id), capture.events.map(row => row.event_id));
assert.equal(await history.locator('.analysis-read-empty').count(), 0,
'No analysis source must not add another explanation card above the recorded history');
const sourceHelp = history.getByRole('button', {name: '分析来源未就绪', exact: true});
Expand Down Expand Up @@ -1006,7 +1007,10 @@ export const researchHistoryScenario = {
await page.locator('.personal-goal-link',{hasText:other.display_name}).click();
assert.equal(new URL(page.url()).searchParams.get('view'), 'research', 'Comparing Goals must keep the research reading context');
await history.getByRole('status').filter({hasText:`${other.events.length} / ${other.events.length}`}).waitFor();
assert.deepEqual(pages.flatMap(value=>value.items).map(row=>row.event_id),other.events.map(row=>row.event_id),`${label}: all original pages, without cross-Goal records`);
// A prior Goal's retry can finish after navigation. Bind observations to
// the requested Goal; foreign records inside its response still fail.
assert.deepEqual(pages.filter(value=>value.goalId===other.goal_id)
.flatMap(value=>value.packet.items).map(row=>row.event_id),other.events.map(row=>row.event_id),`${label}: all original pages, without cross-Goal records`);
assert.equal(await history.locator('.research-history-detail').count(),0, 'A different Goal must not adopt the prior evidence selection');
assert.equal(await history.locator('.research-route').count(),0);
await page.goBack();
Expand Down
2 changes: 1 addition & 1 deletion loopx/canary/module_metric_baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"dict_any_count": 0
},
"loopx/chat_agent.py": {
"lines": 2043
"lines": 2045
},
"loopx/chat_runtime.py": {
"any_count": 38,
Expand Down
8 changes: 5 additions & 3 deletions loopx/chat_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ def _agent_item_text(message: dict[str, Any]) -> str:
"Resolve the exact object and source; an identifier in another repository, an old waiting task or a closed-but-uncompleted object is not proof of the requested outcome. "
"Resolve the intended project, purpose and output destination from the user's request and relevant conversation before matching a role or existing task. A public source or broad collection responsibility does not authorize repurposing private learning notes into an operations library, publication or another project's work. "
"If current evidence shows the requested outcome is already satisfied, explain that result with its source and do not create work, delegate, propose a protected action or repeat the effect. "
"Return available accepted result content in this conversation instead of asking its owner to reproduce it. A status or read marker without the requested content does not itself satisfy delivery; retrieve the missing content through a permitted read or its qualified original owner. "
"A saved source, read receipt or recorded proposal is not proof that the requested outcome works. Separate source duplication, applicable new information and independently verified completion; an unchanged source may still expose unfinished work, while changed bytes may add no useful information. "
"A request for explanation, fact checking, comparison or judgment normally needs your analysis, not automatic assignment. "
"Resolve question-form feedback against the whole conversation and any work already owed. A concrete amendment to established delivery still needs completion here or transfer to its established owner under the current work rules; do not end at agreement or design advice. Preserve standalone explanation and opinion requests as answers, without automatic delegation. "
Expand All @@ -461,6 +462,7 @@ def _agent_item_text(message: dict[str, Any]) -> str:
"Resolve ordinary shorthand against the requested subject, project, purpose and destination. A role alias or matching Agent id is a discovery lead, not a selected recipient. Compare plausible recipients using the permitted directory and relevant current work; when the user means recent research, inspect relevant recent deliveries as well. Disclose a material assumption; ask only when competing interpretations would change the action. Do not ask the user to supply a link or Agent id you can resolve or have an authorized qualified recipient verify. "
"A previous handoff proves where a request was sent, not who owns its subject. When feedback challenges the recipient or corrects the subject, verify that route before preserving an alleged established owner. "
"An explicit Goal/Agent identity or owner correction takes precedence over a similar role name. Preserve the full goal_id/agent_id pair. "
"Do not choose among equally relevant work items because an identifier or its translation repeats their shared topic. Without conversation evidence or an explicit user selection distinguishing their subjects, ask one focused question and keep context_handoff=null. "
"When role names repeat, compare the relevant conversation, established assignment, project and declared responsibilities before deciding that the recipient is ambiguous. Registered host bindings can support that context, but a missing binding does not prove an Agent is offline and an available route cannot override an explicit identity or grant. "
"A corrected handoff does not retract a delivered misroute. Check the original and correction receipts; reuse delivered corrections, and carry any still-needed bounded ownership correction to the original recipient through the existing collaboration path. Preserve actual effects and history; do not claim cancellation or interrupt unrelated work. "
"For forwarded or quoted requests, preserve the original speaker and addressee: 'you' does not automatically refer to the receiving Agent. Explain the resolved referent in the existing collaboration brief when it matters; do not invent one. "
Expand Down Expand Up @@ -654,14 +656,14 @@ def _turn_prompt(
"Then append exactly one machine-readable envelope with message set to the empty string: the transport reuses the visible answer before the opening tag. Do not repeat the answer in JSON. This envelope is hidden protocol metadata and is required even for ordinary questions or exact-wording replies; user formatting instructions govern the visible answer, not omission of this metadata. "
"protected_action must be null or an object shaped as "
'{"operation":"merge|release|deploy|delete|payment","target":"user-stated target","summary":"short public-safe proposal"}. '
"Do not write anything after the closing tag. Use these tags and shape:\n"
f"{CHAT_REVIEW_OPEN_TAG}{json.dumps(envelope, ensure_ascii=False)}{CHAT_REVIEW_CLOSE_TAG}\n\n"
+ (
f"LoopX context (supporting context only):\n{context_summary.strip()}\n\n"
if context_summary.strip()
else ""
)
+ f"Operator user message:\n{user_message}"
+ f"Operator user message:\n{user_message}\n\n"
+ "After the visible answer, append the envelope below. Use both tags verbatim, including when returning an existing report. Write nothing after the closing tag.\n"
f"{CHAT_REVIEW_OPEN_TAG}{json.dumps(envelope, ensure_ascii=False)}{CHAT_REVIEW_CLOSE_TAG}"
)


Expand Down
31 changes: 3 additions & 28 deletions loopx/cli_commands/project_lifecycle_refresh_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
stage_reward_memory_outcome_candidate,
)
from ..control_plane.quota.cli_projection import compact_refresh_state_cli_payload
from .refresh_settlement import settle_requested_refresh


def register_refresh_state_command(
Expand Down Expand Up @@ -882,7 +883,7 @@ def handle_refresh_state_command(
)
)
if not material_refresh_ready:
_settle_requested_refresh(payload, args, registry_path, goal_ref)
settle_requested_refresh(payload, args, registry_path, goal_ref)
print_payload(compact_refresh_state_cli_payload(payload) if fmt == "json" else payload, fmt, render_state_refresh_markdown)
return 0 if payload.get("ok") else 1
if payload.get("explore_result") is not None:
Expand Down Expand Up @@ -972,32 +973,6 @@ def handle_refresh_state_command(
)
),
)
_settle_requested_refresh(payload, args, registry_path, goal_ref)
settle_requested_refresh(payload, args, registry_path, goal_ref)
print_payload(compact_refresh_state_cli_payload(payload) if fmt == "json" else payload, fmt, render_state_refresh_markdown)
return 0 if payload.get("ok") else 1


def _settle_requested_refresh(payload, args, registry_path, goal_ref):
if not getattr(args, "settle_turn", False):
return
from .refresh_settlement import present_settled_refresh, settle_refreshed_turn

runtime_root = resolve_runtime_root(load_registry(registry_path), args.runtime_root)
settle_refreshed_turn(payload, registry_path=registry_path, runtime_root=runtime_root,
goal_id=args.goal_id, agent_id=args.agent_id, todo_id=args.todo_id,
replan_obligation_id=args.replan_obligation_id,
turn_instance_id=args.turn_instance_id, goal_ref=goal_ref,
delivery_workspace_path=(
Path(args.delivery_workspace_path)
if args.delivery_workspace_path else None
))
if getattr(args, "settlement_receipt", False):
from ..control_plane.quota.error_codes import QuotaDecisionTransportError
try:
view = present_settled_refresh(payload, runtime_root=runtime_root)
except QuotaDecisionTransportError as exc:
payload.update(ok=False, error=str(exc))
return # Exact committed receipts stay visible; never re-admit.
if view is not payload:
payload.clear()
payload.update(view)
Loading
Loading