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
Original file line number Diff line number Diff line change
Expand Up @@ -2889,6 +2889,8 @@ source paths, authorize monitor writeback, or change provider/promotion holds.

**D1 — qualify permanent projection delivery; may overlap T1/T2.**

The Goal Channel ownership observation consumes one complete provider revision before bounding display. It never repairs Markdown or revives old local leases; provider failures and truncation stay visible. This is a T3 read closure with shared TS interpretation, not D1/D2 qualification or D3 cutover. See [coordination observation](../../reference/coordination-observation.md).

The D1 document-ownership slice gives readers, editors and projection one visible-region
and Todo-block boundary. It fixes fenced examples becoming real tasks, narrative after
an archive end marker entering history, and sparse imported ordinals or archived
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2286,6 +2286,8 @@ Scoped fallback 的选择与门禁关系也已复用同一 TS decision owner,

**D1 — 资格化永久投影交付,可与 T1/T2 重叠推进。**

Goal Channel 所有权观察先读取完整 provider revision,再限制展示;不修复 Markdown、不复活旧本地 lease,明确披露失败与截断。这是共用 TS 解释规则的 T3 读链路闭合,不完成 D1/D2 或 D3 切换,见 [coordination observation](../../reference/coordination-observation.md)。

D1 的文档归属切片把读取、编辑与投影放到同一可见区域/Todo 行解码边界,修复
fenced 示例被当成真实任务、归档 end marker 后叙述进入历史、稀疏历史行号及归档
优先级阻塞读回的问题。投影复用普通状态的耐久原子写入;相同字节的重试仍完成
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,8 @@ delivery. This does not finish all T2 commands or authorize whole-Goal promotion

**T3 — close remaining structured consumers, then remove their old reads.**

Goal Channel ownership observation now reads a complete canonical Todo/lease revision and shares one TS batch policy with the legacy adapter. It retires display-layer lease time/generation/conflict decisions and local-file reads after promotion. Empty, unavailable and truncated observations remain distinct; see [coordination observation](../../reference/coordination-observation.md). This closes the Goal Channel ownership reader, not other channel panels or whole-Goal promotion.

The D1 document-ownership slice gives readers, editors and projection one visible-region
and Todo-block boundary. It fixes fenced examples becoming real tasks, narrative after
an archive end marker entering history, and sparse imported ordinals or archived
Expand Down Expand Up @@ -664,7 +666,7 @@ derived inside acquire from the supplied owner/claim/exclusion/registration fact
not from the old caller-provided `effective` hint. Other-Todo overlap facts still
come from the existing complete execution snapshot; release retains its separate
key/version cleanup fence. This closes one T3 reader and shared rule boundary,
not the remaining Goal-channel lease display, T1/T2 transactions or promotion.
not the remaining T1/T2 transactions or promotion. Goal Channel ownership display closes in the separate ownership-observation slice.

Capability resolution now shares `agents/capability_gate.ts`: missing prerequisites,
repair outputs, owner/agent resolution and blocked-Todo bindings have one typed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,8 @@ promotion 已完成。

**T3 — 闭合剩余 structured consumer,删除各自旧读路径。**

Goal Channel 所有权观察现从完整 canonical Todo/lease revision 读取,并与 legacy adapter 共用 TS 批量规则;删除展示层的时间/代数/冲突判断和晋升后的本地文件读路径。空值、不可用与截断分别披露,见 [coordination observation](../../reference/coordination-observation.md)。这只闭合所有权观察 reader,不宣称其余面板或整 Goal 晋升完成。

D1 的文档归属切片把读取、编辑与投影放到同一可见区域/Todo 行解码边界,修复
fenced 示例被当成真实任务、归档 end marker 后叙述进入历史、稀疏历史行号及归档
优先级阻塞读回的问题。投影复用普通状态的耐久原子写入;相同字节的重试仍完成
Expand All @@ -515,8 +517,8 @@ handoff mode;canonical 无租约不复活本地旧文件,provider 失败不
供 acquire、lifecycle 与终态 fence 复用。当前租约是否有效由 acquire 内部根据同一输入
的 owner/claim/exclusion/注册事实推导,不再由旧 `effective` 派生提示覆盖。
其他 Todo 的 scope 冲突仍消费现有完整执行快照;release 保留独立的 key/version
清理门禁。这是一个 T3 reader 与共享规则边界的闭合,不代表 Goal-channel lease
展示、T1/T2 全部事务或 promotion 已完成。
清理门禁。这是一个 T3 reader 与共享规则边界的闭合,不代表 T1/T2 全部事务或
promotion 已完成;Goal Channel 所有权展示由独立的 observation 切片闭合。

Quota 的 scope/claim 消费者现通过每个 source 一次 `todo.quota_planning.project`,
组合选择、有限展示与既有 resume planner。`quota_selection.ts` 替代 Python
Expand Down
Binary file added docs/assets/coordination-observation-before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
87 changes: 87 additions & 0 deletions docs/reference/coordination-observation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Goal Channel coordination observation

Goal Channel's `active_leases` is a read-only display of Todo claims and
time-active lease records. A displayed lease is not an execution grant: actual
mutation still checks the owning operation's eligibility, mode and lease fence.

```bash
loopx status --goal-id example-goal --format json
```

Inspect `goal_channel_projection.active_leases` and its `source_warnings` in the
status/attention item. No new capability activation or provider selection is
required. Existing Goal Channel HTML renders these rows and warnings; this read
never sends a message or changes channel bindings.

Before canonical promotion, the source remains the supplied status Todo view
plus local lease files. Python adapts file records; one TS request evaluates
expiry, lease generation and claim conflicts for the whole batch. The legacy
Todo view can be incomplete and does not acquire canonical completeness by using
the shared rule. An explicitly supplied empty `active_leases=[]` does not cause
soft claims to be inferred; local hard-lease observation remains independent.

After promotion, the selected provider supplies the complete Todo/lease snapshot
in one read. Stale or absent Markdown, legacy lease files and caller-supplied
claim/lease display overrides cannot replace canonical ownership facts. An empty
canonical result stays empty. Claims of completed/archived Todos do not re-enter
the active claim display. Canonical claim/lease conflict comparison happens
before output limits or text redaction.

`coordination_observation` records the provider source/revision, observation time,
record counts, total observations, display limit and truncation. These fields
apply only to the ownership observation; other Goal Channel panels remain their
existing status/quota/history projections and need not share that revision.
The canonical display limit is 100 entries, after evaluating the complete source;
corrupt-lease and conflict diagnostics come first. A truncation warning means the
visible rows are not a complete work inventory.

All leases use one observation time. Expiry equal to that time is expired.
Malformed active expiry, unknown schema, mismatched lease identity and invalid
generation produce `hard_lease_unreadable` / `corrupt_lease` observations instead
of silently disappearing or crashing the channel. A provider/protocol failure
produces an empty ownership list with `coordination_unavailable`; that empty list
is **not evidence of no ownership**. Raw errors, lease operation keys, write
scopes and arbitrary backend metadata are not copied into canonical display.
Existing channel text redaction still applies.

The read performs no business mutation, receipt creation, Markdown repair,
promotion or fallback write. It does not change provider defaults or qualify a
PostgreSQL CLI selector, whole-Goal cutover or long-duration SQLite storage.
Disable/rollback follows the existing provider lifecycle; do not revive stale
local files to bypass an unavailable canonical source.

## Recognize an unavailable source

The same synthetic unavailable canonical source and stale local lease, rendered
by the pinned legacy implementation (left) and the provider-aware reader (right).
The corrected panel explicitly reports an unavailable observation rather than
presenting the local lease as current ownership. Source Warnings carries the
additional explanation. Desktop and mobile layouts use the existing renderer.

| Legacy display | Provider-aware display |
| --- | --- |
| ![Stale local lease shown as ownership](../assets/coordination-observation-before.png) | ![Canonical ownership unavailable](../assets/coordination-observation-unavailable.png) |

## 中文

Goal Channel 的 `active_leases` 展示认领与时间上有效的 lease 记录,不授予执行权;
实际修改仍受相应操作的资格、mode 和 lease 门禁约束。上面的 status 命令可读取
现有 Goal Channel/attention 投影,不新增 activation,也不发送消息。

晋升前沿用传入的状态 Todo 视图与本地 lease 文件,由同一 TS 批量规则计算时间、
代数和认领冲突;旧 Todo 视图仍可能不完整。明确传入空列表不再补出 soft claim,
本地 hard lease 仍独立观察。

晋升后从选定 provider 的完整同一 revision 读取所有权事实。陈旧/缺失 Markdown、
旧 lease 文件或调用方的展示覆盖值不再成为事实来源;canonical 空值保持为空,
完成/归档 Todo 的认领不进入活动认领展示。先判断完整数据中的冲突,再脱敏与截断。

`coordination_observation` 披露来源、版本、观察时间、总数和截断情况,只描述
所有权这一组数据,不声称整张 Goal Channel 的所有面板来自同一 revision。
最多展示 100 条,异常/冲突优先;截断提示意味着不能把可见列表当作完整工作清单。
所有 lease 使用同一个观察时间,到期时间恰好相等视为过期。损坏记录显示不可读提示;
provider 失败显示 `coordination_unavailable`,不会回退本地文件,也不把空列表说成无人负责。
原始错误、操作密钥和任意后端字段不进入展示,现有文本脱敏继续生效。

这是一条只读链路,不修改权威状态、回执或 Markdown,不改变默认 provider,也不
完成 SQLite 长程资格化、PostgreSQL CLI 选择入口或整 Goal 切换。
20 changes: 20 additions & 0 deletions loopx/control_plane/coordination/local_authority_runtime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {COORDINATION_TODO_ARCHIVE_RESULT_SCHEMA} from "./todo_archive.ts";
import {readCoordinationOwnership} from "./ownership_observation.ts";
import {executeTodoContinuation} from "./todo_continuation.ts";
import { withFileMutationLock } from "../effect_runtime_io.ts";
import { ShadowManagementError, requireShadowPrimaryWriteAllowed, shadowMaintenanceLockPath } from "./shadow_management.ts";
Expand Down Expand Up @@ -1207,3 +1208,22 @@ export async function continueLocalTodo(value: unknown): Promise<JsonObject> {
...localAuthorityOpenFailure(error)};
}
}

/** Goal Channel observes a complete provider snapshot through one coarse read. */
export async function observeLocalCoordinationOwnership(value: unknown): Promise<JsonObject> {
let sourceAuthority = "canonical_unavailable";
try {
const input = requireJsonObject(value, "local ownership observation");
if (input.schema_version !== "loopx_local_ownership_observation_request_v0") throw new Error("ownership observation schema mismatch");
const root = runtimeRoot(input.runtime_root);
const goalId = requireAuthorityStoreId(input.goal_id, "goal id");
const store = await openLocalAuthorityStore(root, goalId);
sourceAuthority = sourceAuthorityFor(store);
return {...await readCoordinationOwnership(store, goalId, input.observed_at as string),
source_authority: sourceAuthority, decision_read_from_provider: true, legacy_fallback_used: false};
} catch (error) {
return {schema_version: "loopx_ownership_observation_result_v0", status: "failed",
reason_code: "coordination_observation_unavailable", source_authority: sourceAuthority,
decision_read_from_provider: true, legacy_fallback_used: false, ...localAuthorityOpenFailure(error)};
}
}
95 changes: 95 additions & 0 deletions loopx/control_plane/coordination/ownership_observation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/** Read-only ownership observations. These records describe claims/leases, never grant execution. */
import type {JsonObject} from "../effect_program.ts";
import type {AuthorityStore} from "./authority_store.ts";
import {requireJsonObject} from "../runtime_decode.ts";
import {parseIsoTimestamp} from "../runtime_timestamp.ts";
import {requireAuthorityStoreId} from "./authority_store_codec.ts";
import {indexCoordinationProjection, validateCoordinationTodoReadModel} from "./coordination_projection.ts";
import {leaseEpoch, leaseIsActive, TASK_LEASE_SCHEMA_VERSION} from "../work_items/task_lease_acquire.ts";

export const OWNERSHIP_OBSERVATION_SCHEMA = "loopx_ownership_observation_request_v0";
export const OWNERSHIP_OBSERVATION_RESULT = "loopx_ownership_observation_result_v0";
export const CANONICAL_OWNERSHIP_DISPLAY_LIMIT = 100;

type ObservationStatus = "soft_claim" | "hard_lease" | "hard_lease_unreadable";
function objects(value: unknown, label: string): JsonObject[] {
if (!Array.isArray(value)) throw new Error(`${label} must be an array`);
return value.map(item => requireJsonObject(item, label));
}
function text(value: unknown): string | null {
return typeof value === "string" && value.trim() ? value.trim() : null;
}
function note(todoId: string): JsonObject {
const status: ObservationStatus = "hard_lease_unreadable";
return {todo_id: todoId, status, reason: "corrupt_lease"};
}

function displayEntry(item: JsonObject): JsonObject {
const entry: JsonObject = {};
for (const key of ["todo_id", "owner_agent", "claimed_by", "lease_until", "expires_at", "status", "reason"]) {
const value = text(item[key]);
if (value) entry[key] = value;
}
for (const key of ["lease_version", "lease_epoch"]) {
if (typeof item[key] === "number" && Number.isSafeInteger(item[key])) entry[key] = item[key];
}
return entry;
}

/** Both source adapters share time/generation/conflict rules; explicit [] is authoritative. */
export function projectOwnershipObservation(value: unknown): JsonObject {

Check failure on line 40 in loopx/control_plane/coordination/ownership_observation.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 28 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaCZLwM76BeFNClEVauQ&open=AaCZLwM76BeFNClEVauQ&pullRequest=4316
const input = requireJsonObject(value, "ownership observation");
if (input.schema_version !== OWNERSHIP_OBSERVATION_SCHEMA) throw new Error("ownership observation schema mismatch");
const at = typeof input.observed_at === "string" ? parseIsoTimestamp(input.observed_at) : null;
if (at === null) throw new Error("observed_at must be a valid timestamp");
const todos = objects(input.todos, "todos");
const claims = new Map(todos.map(todo => [text(todo.todo_id), text(todo.claimed_by)]));
const explicit = input.explicit_entries == null ? null : objects(input.explicit_entries, "explicit_entries");
const entries: JsonObject[] = explicit === null ? todos.filter(todo => text(todo.claimed_by)).map(todo => ({
todo_id: todo.todo_id ?? null, owner_agent: todo.claimed_by!, status: "soft_claim" satisfies ObservationStatus,
})) : explicit.map(displayEntry).filter(entry => text(entry.todo_id) || text(entry.owner_agent) || text(entry.claimed_by));
for (const row of objects(input.lease_rows, "lease_rows")) {
const todoId = text(row.todo_id);
if (!todoId) throw new Error("lease observation requires a Todo identity");
if (row.unreadable === true) {entries.push(note(todoId)); continue;}
const lease = row.lease == null ? null : requireJsonObject(row.lease, "lease");
if (lease === null) continue;
try {
if (lease.schema_version !== TASK_LEASE_SCHEMA_VERSION || lease.todo_id !== todoId) throw new Error("lease identity/schema mismatch");
if (!leaseIsActive(lease, at)) continue;
const entry: JsonObject = {todo_id: todoId, status: "hard_lease" satisfies ObservationStatus,
lease_epoch: leaseEpoch(lease), expires_at: lease.expires_at!};
const owner = text(lease.owner);
if (owner) entry.owner_agent = owner;
if (typeof lease.version === "number" && Number.isInteger(lease.version)) entry.lease_version = lease.version;
const claim = claims.get(todoId);
if (owner && claim && owner !== claim) {entry.reason = "owner_conflicts_with_claim"; entry.claimed_by = claim;}
entries.push(entry);
} catch { entries.push(note(todoId)); }
}
return {schema_version: OWNERSHIP_OBSERVATION_RESULT, status: "loaded", entries,
total_count: entries.length, observed_at: input.observed_at!};
}

/** One complete, validated revision; no display, local files, receipts or writes. */
export async function readCoordinationOwnership(store: AuthorityStore, goalId: string, observedAt: string): Promise<JsonObject> {
requireAuthorityStoreId(goalId, "goal id");
const loaded = await store.loadAuthority();
if (loaded.status !== "loaded") return {schema_version: OWNERSHIP_OBSERVATION_RESULT, ...loaded};
const index = indexCoordinationProjection(loaded.head, goalId);
validateCoordinationTodoReadModel(loaded.head, goalId);
const todos = [...index.todos.values()].filter(todo => todo.archive_state === "active" && todo.done !== true);
const result = projectOwnershipObservation({schema_version: OWNERSHIP_OBSERVATION_SCHEMA,
observed_at: observedAt, todos, explicit_entries: todos.filter(todo => todo.role === "agent" && text(todo.claimed_by))
.map(todo => ({todo_id: todo.todo_id, owner_agent: todo.claimed_by!, status: "soft_claim"})),
lease_rows: [...index.leases.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)

Check warning on line 85 in loopx/control_plane/coordination/ownership_observation.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaCZLwM76BeFNClEVauR&open=AaCZLwM76BeFNClEVauR&pullRequest=4316
.map(([todo_id, lease]) => ({todo_id, lease})),
});
// Evaluate the whole snapshot before bounding display; diagnostics are retained first.
const entries = result.entries as JsonObject[];
const ordered = [...entries.filter(row => row.reason), ...entries.filter(row => !row.reason)];
return {...result, entries: ordered.slice(0, CANONICAL_OWNERSHIP_DISPLAY_LIMIT),
truncated: entries.length > CANONICAL_OWNERSHIP_DISPLAY_LIMIT, display_limit: CANONICAL_OWNERSHIP_DISPLAY_LIMIT,
todo_count: index.todos.size, lease_count: index.leases.size,
provider_revision: loaded.provider_revision, cursor: loaded.cursor};
}
4 changes: 4 additions & 0 deletions loopx/control_plane/effect_runtime_handlers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {planHandoffMode} from "./coordination/handoff_mode_policy.ts";
import {setLocalHandoffMode} from "./coordination/handoff_mode_runtime.ts";
import {projectOwnershipObservation} from "./coordination/ownership_observation.ts";
import {observeLocalCoordinationOwnership} from "./coordination/local_authority_runtime.ts";

Check warning on line 4 in loopx/control_plane/effect_runtime_handlers.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'/home/runner/work/loopx/loopx/loopx/control_plane/coordination/local_authority_runtime.ts' imported multiple times.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaCZLwNe6BeFNClEVauS&open=AaCZLwNe6BeFNClEVauS&pullRequest=4316
import {evaluateTaskLeaseOwnerEligibility} from "./work_items/task_lease_eligibility.ts";
import { evaluateSubagentContext, describeSubagentContext } from "./subagent_context.ts";
import {
Expand Down Expand Up @@ -141,7 +143,7 @@
promoteLocalCoordinationAuthority,
readLocalCoordinationTodo,
terminalLifecycleLocalCoordinationTodo,
} from "./coordination/local_authority_runtime.ts";

Check warning on line 146 in loopx/control_plane/effect_runtime_handlers.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

'/home/runner/work/loopx/loopx/loopx/control_plane/coordination/local_authority_runtime.ts' imported multiple times.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaCZLwNe6BeFNClEVauT&open=AaCZLwNe6BeFNClEVauT&pullRequest=4316
import { evaluateCoordinationTodoClaimDecision } from "./coordination/todo_claim.ts";
import {
evaluateCoordinationTodoTerminalDecision,
Expand Down Expand Up @@ -484,6 +486,8 @@
["coordination.local_authority.todo_compatibility_edit", editLocalCoordinationTodo],
["coordination.local_authority.mutate", mutateLocalCoordinationAuthority],
["coordination.local_authority.todo_read", readLocalCoordinationTodo],
["coordination.ownership_observation", projectOwnershipObservation],
["coordination.local_authority.ownership_observation", observeLocalCoordinationOwnership],
["coordination.local_authority.todo_list", listLocalCoordinationTodos],
[
"coordination.local_authority.legacy_writer_fence.engage",
Expand Down
Loading