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
16 changes: 16 additions & 0 deletions docs/architecture/rfcs/typescript-control-plane-migration-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@

## Current implementation checkpoint

The projection-delivery stage now closes the cross-language boundary: typed
TypeScript mutation results and the Python compatibility provider share the
same four-state contract (`pending`, `delivered`, `current`, `not_required`).
Provider readback is validated before acknowledgement decisions, and the
end-to-end causal chain is covered by a shared composition fixture. This is a
completed delivery stage, not a promotion of Markdown or a claim that the
remaining lifecycle writers have migrated.

The same stage also removes duplicated Python read policy around that boundary.
Task-class resolution, title-aware actionability, dependency readiness, agent
eligibility, priority ordering, and canonical Todo read records now have one
Python semantic owner while TypeScript remains the transaction owner. The old
projection module is an import-only compatibility facade. This keeps the
replacement-first rule intact: compatibility remains available, but it cannot
silently become a second semantic implementation.

Native update now composes `todos/public_update.ts` for a bounded nonterminal
planning intent (status, evidence/reason, resume/clear and successor links),
against the same complete canonical head used for authority checks and CAS.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@

## 当前实现检查点

投影交付阶段现已闭合跨语言边界:typed TypeScript mutation 结果与 Python
兼容 provider 共用四态契约(`pending`、`delivered`、`current`、`not_required`)。
Provider readback 在 acknowledgement 决策前进行校验,端到端因果链由共享组合
fixture 覆盖。这是一个已完成的交付阶段,不代表 Markdown 晋升,也不声称其余
lifecycle writer 已全部迁移。

同一阶段也删除了该边界周围重复的 Python read policy。task-class 解析、识别 title
的 actionable 判断、依赖就绪、Agent eligibility、priority 排序和 canonical Todo
read record 现在只有一个 Python 语义 owner,而 TypeScript 仍是事务 owner。旧
projection 模块只保留 import-only 兼容 facade。这样继续遵守 replacement-first:
兼容路径仍可用,但不能静默形成第二份语义实现。

Native update 现通过 `todos/public_update.ts` 组合有界的非终态 planning intent
(status、evidence/reason、resume/clear、successor links),使用权限检查与 CAS
同一份完整 canonical head。独立 intent 命名空间不扩大原 text/note patch allowlist,
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/contracts/interface-budget-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ and size/count budgets.
| `heartbeat_prompt_json` | heartbeat automation | wake and route one bounded turn | `quota should-run`, `status`, or `review-packet --handoff-only` | `json_chars <= 4800` plus `interface_budget.within_budget=true` | `nested_keys <= 40` | `top_level_keys <= 30` |
| `review_packet_handoff_only_json` | project-agent handoff | forward the smallest sufficient task packet | full `review-packet` or run-history artifact | `json_chars <= 3000` plus `handoff_interface_budget.within_budget=true` | `nested_keys <= 40` | `top_level_keys <= 18` |
| `quota_should_run_json` | quota guard | decide whether the selected goal may spend compute | `status`, `history`, or active state | `json_chars <= 13000` | `nested_keys <= 330` | `top_level_keys <= 52` |
| `dashboard_status_json` | operator dashboard | render first-screen operator state | `history`, run artifacts, or project-local adapter output | `json_chars <= 18500` | `nested_keys <= 260` | `top_level_keys <= 25` |
| `dashboard_status_json` | operator dashboard | render first-screen operator state | `history`, run artifacts, or project-local adapter output | `json_chars <= 19500` | `nested_keys <= 260` | `top_level_keys <= 25` |

These four budgets measure compact machine payloads. For
`heartbeat_prompt_json`, the measured payload is the actual
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
"owner": "operator dashboard",
"consumer": "render first-screen operator state",
"cold_path": "history, run artifacts, or project-local adapter output",
"max_json_chars": 18_500,
"max_json_chars": 19_500,
"max_nested_keys": 260,
"max_top_level_keys": 25,
},
Expand Down
2 changes: 1 addition & 1 deletion loopx/capabilities/explore/composition_frontier.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
normalize_explore_result_node_refs,
normalize_todo_claimed_by,
)
from ...control_plane.todos.projection import (
from ...control_plane.todos.todo_semantics import (
todo_item_is_actionable_open,
todo_item_task_class,
)
Expand Down
2 changes: 1 addition & 1 deletion loopx/capabilities/explore/todo_branch_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
normalize_todo_id,
normalize_todo_status,
)
from ...control_plane.todos.projection import (
from ...control_plane.todos.todo_semantics import (
todo_item_is_actionable_open,
todo_item_task_class,
todo_priority_rank,
Expand Down
2 changes: 1 addition & 1 deletion loopx/capabilities/explore/worker_branch_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
_shared_dependency_capabilities,
_scopes_overlap,
)
from ...control_plane.todos.projection import todo_item_task_class, todo_projection_sort_key
from ...control_plane.todos.todo_semantics import todo_item_task_class, todo_projection_sort_key


WORKER_BRANCH_PLAN_SCHEMA_VERSION = "loopx_explore_worker_branch_plan_v0"
Expand Down
2 changes: 1 addition & 1 deletion loopx/capabilities/issue_fix/pr_gate_reconcile.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ...control_plane.todos.contract import (
normalize_todo_decision_scope,
)
from ...control_plane.todos.projection import todo_item_task_class
from ...control_plane.todos.todo_semantics import todo_item_task_class
from ...todos import complete_goal_todo, list_goal_todos
from .pr_lifecycle import build_issue_fix_pr_lifecycle_monitor_packet
from .pr_lifecycle_rollout import append_pr_merge_rollout_event
Expand Down
2 changes: 1 addition & 1 deletion loopx/capabilities/issue_fix/pr_review_ack.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
TODO_TASK_CLASS_USER_ACTION,
normalize_todo_bound_agent,
)
from ...control_plane.todos.projection import todo_item_task_class
from ...control_plane.todos.todo_semantics import todo_item_task_class
from ...history import load_registry
from ...paths import resolve_runtime_root
from ...rollout_event_log import (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Any

from ...control_plane.todos.active_state_todo_parser import parse_active_state_todos
from ...control_plane.todos.projection import todo_item_is_actionable_open
from ...control_plane.todos.todo_semantics import todo_item_is_actionable_open
from ...registry import find_registry_goal, read_json, resolve_state_file
from .incremental import select_incremental_project_progress

Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/agents/agent_lane_recommendation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
normalize_todo_claimed_by,
normalize_todo_id,
)
from ..todos.projection import todo_item_is_due_monitor
from ..todos.todo_semantics import todo_item_is_due_monitor
from ..todos.summary_item import compact_todo_summary_item
from ..work_items.primary_action import protocol_action_text
from ..work_items.work_lane import (
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/agents/agent_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
)
from ..todos.handoff_gate import HandoffGateState
from ..todos.resume_planning import project_todo_resume_planning
from ..todos.projection import (
from ..todos.todo_semantics import (
todo_item_claimed_by_agent_or_unclaimed,
todo_item_excludes_agent,
todo_item_is_actionable_open,
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/agents/capability_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
normalize_target_capabilities,
normalize_todo_claimed_by,
)
from ..todos.projection import (
from ..todos.todo_semantics import (
todo_index_rank,
todo_item_is_actionable_open,
todo_item_task_class,
Expand Down
3 changes: 2 additions & 1 deletion loopx/control_plane/coordination/todo_claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
requireAuthorityStoreId,
} from "./authority_store_codec.ts";
import {validateContinuationNote, computeContinuationTodoFacts} from "./continuation_note.ts";
import {projectionDelivery} from "../todos/projection_delivery.ts";
import {normalizeRegisteredTodoAgents, normalizeTodoAgent} from "./todo_agents.ts";
import {
prepareCoordinationProjectionCommit,
Expand Down Expand Up @@ -450,7 +451,7 @@ export async function executeCoordinationTodoClaim(
provider_revision: receipt.provider_revision,
cursor: receipt.cursor,
original_receipt: original,
projection_delivery: result.changed === false ? "not_required" : "pending",
projection_delivery: projectionDelivery(result.changed !== false),
projection_source: "committed_authority_journal",
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { JsonObject } from "../effect_program.ts";
import type { AuthorityStore } from "./authority_store.ts";
import { canonicalAuthorityBytes, canonicalAuthorityObject, canonicalAuthoritySha256, requireAuthorityStoreId } from "./authority_store_codec.ts";
import { prepareCoordinationProjectionCommit, indexCoordinationProjection, validateCoordinationTodoReadModel } from "./coordination_projection.ts";
import { projectionDelivery } from "../todos/projection_delivery.ts";

export const TODO_COMPATIBILITY_EDIT_SCHEMA = "loopx_todo_compatibility_edit_request_v0";
export const TODO_COMPATIBILITY_EDIT_RESULT_SCHEMA = "loopx_todo_compatibility_edit_result_v0";
Expand Down Expand Up @@ -75,7 +76,7 @@ export async function editCoordinationTodo(
status: status === "applied" && !original.changed ? "no_change" : status,
changed: status !== "replayed" && original.changed,
provider_revision: receipt.provider_revision, cursor: receipt.cursor,
projection_delivery: original.changed ? "pending" : "not_required",
projection_delivery: projectionDelivery(original.changed),
projection_source: "committed_authority_journal",
};
};
Expand Down
3 changes: 2 additions & 1 deletion loopx/control_plane/coordination/todo_create.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { JsonObject } from "../effect_program.ts";
import {projectionDelivery} from "../todos/projection_delivery.ts";
import type { AuthorityStore, AuthorityStoreReceiptResult } from "./authority_store.ts";
import {
AuthorityStoreProtocolError,
Expand Down Expand Up @@ -77,7 +78,7 @@ function replayCreate(
provider_revision: receipt.provider_revision,
cursor: receipt.cursor,
original_receipt: original,
projection_delivery: "pending",
projection_delivery: projectionDelivery(true),
projection_source: "committed_authority_journal",
};
}
Expand Down
3 changes: 2 additions & 1 deletion loopx/control_plane/coordination/todo_monitor_poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {planMonitorMetadata, TODO_MONITOR_METADATA_REQUEST_SCHEMA} from "../todo
import {planMonitorSuccessor, selectMonitorTodo, MONITOR_SUCCESSOR_REQUEST_SCHEMA} from "../scheduler/monitor_successor.ts";
import {optionalNonEmptyString, requireBoolean} from "../runtime_decode.ts";
import {planTodoAuthoringScope, TODO_AUTHORING_SCOPE_REQUEST_SCHEMA} from "../todos/authoring_scope.ts";
import {projectionDelivery} from "../todos/projection_delivery.ts";

export const COORDINATION_MONITOR_POLL_REQUEST_SCHEMA = "loopx_coordination_monitor_poll_request_v0";
export const COORDINATION_MONITOR_POLL_RESULT_SCHEMA = "loopx_coordination_monitor_poll_result_v0";
Expand Down Expand Up @@ -42,7 +43,7 @@ function replay(receipt: AuthorityStoreReceiptResult, input: CoordinationMonitor
return {schema_version: COORDINATION_MONITOR_POLL_RESULT_SCHEMA, status,
changed: status !== "replayed", provider_revision: receipt.provider_revision, cursor: receipt.cursor,
writeback: {...canonicalAuthorityObject(original.writeback, "Monitor writeback"), provider_replayed: status === "replayed"},
projection_delivery: "pending", projection_source: "committed_authority_journal"};
projection_delivery: projectionDelivery(true), projection_source: "committed_authority_journal"};
}

function normalize(raw: CoordinationMonitorPollInput): CoordinationMonitorPollInput {
Expand Down
5 changes: 3 additions & 2 deletions loopx/control_plane/coordination/todo_terminal_lifecycle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createHash } from "node:crypto";
import {projectionDelivery} from "../todos/projection_delivery.ts";

import type { JsonObject } from "../effect_program.ts";
import type {
Expand Down Expand Up @@ -451,7 +452,7 @@ function replayTerminal(
provider_revision: receipt.provider_revision,
cursor: receipt.cursor,
original_receipt: original,
projection_delivery: result.changed === true ? "pending" : "not_required",
projection_delivery: projectionDelivery(result.changed === true),
projection_source: "committed_authority_journal",
};
}
Expand Down Expand Up @@ -1134,7 +1135,7 @@ function replayArchive(
provider_revision: receipt.provider_revision,
cursor: receipt.cursor,
original_receipt: original,
projection_delivery: result.changed === true ? "pending" : "not_required",
projection_delivery: projectionDelivery(result.changed === true),
projection_source: "committed_authority_journal",
};
}
Expand Down
3 changes: 2 additions & 1 deletion loopx/control_plane/coordination/todo_update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { evaluateCoordinationTerminalFence, COORDINATION_TERMINAL_FENCE_REQUEST_
import { leaseEpoch } from "../work_items/task_lease_acquire.ts";
import { parseIsoTimestamp } from "../runtime_timestamp.ts";
import { normalizeNativePlanningIntent, planNativeTodoUpdate } from "../todos/native_update_plan.ts";
import { projectionDelivery } from "../todos/projection_delivery.ts";

export const COORDINATION_TODO_UPDATE_REQUEST_SCHEMA =
"loopx_local_coordination_todo_update_request_v0";
Expand Down Expand Up @@ -139,7 +140,7 @@ function replayUpdate(
changed: status !== "replayed" && original.changed,
todo_id: input.todo_id, provider_revision: receipt.provider_revision,
cursor: receipt.cursor, original_receipt: original,
projection_delivery: original.changed ? "pending" : "not_required",
projection_delivery: projectionDelivery(original.changed),
projection_source: "committed_authority_journal"};
}

Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/goals/goal_frontier/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ...agents.runtime_model import peer_work_key, select_peer_for_work
from ...runtime.time import parse_timestamp
from ...todos.contract import normalize_todo_replan_obligation_id
from ...todos.projection import (
from ...todos.todo_semantics import (
todo_advancement_frontier_counts,
todo_item_is_watch_only_monitor,
)
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/goals/goal_frontier/ack_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
normalize_todo_replan_obligation_id,
replan_successor_semantic_binding,
)
from ...todos.projection import todo_item_is_actionable_open, todo_item_task_class
from ...todos.todo_semantics import todo_item_is_actionable_open, todo_item_task_class
from ...work_items.progress_observation import (
replan_obligation_trigger_checkpoints,
replan_obligation_trigger_kinds,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
normalize_todo_id,
)
from ...todos.resume_planning import project_todo_resume_planning
from ...todos.projection import (
from ...todos.todo_semantics import (
agent_scoped_selectable_advancement_todo_ids,
)
from ..goal_vision_read_model import (
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/goals/goal_vision_wait.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Any

from ..effect_runtime import effect_runtime_result
from ..todos.projection import todo_item_excludes_agent
from ..todos.todo_semantics import todo_item_excludes_agent
from ..todos.contract import (
TODO_TASK_CLASS_BLOCKER,
normalize_todo_claimed_by,
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/goals/goal_vision_wait_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from typing import Any

from ..todos.projection import agent_scoped_selectable_advancement_todo_ids
from ..todos.todo_semantics import agent_scoped_selectable_advancement_todo_ids
from .goal_vision_read_model import (
acceptance_gaps_from_agent_vision,
latest_agent_vision_from_runs,
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/goals/start_goal_todo_delta.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from ...control_plane.todos.contract import (
TODO_TASK_CLASS_ADVANCEMENT,
)
from ...control_plane.todos.projection import todo_item_is_actionable_open
from ...control_plane.todos.todo_semantics import todo_item_is_actionable_open
from ...project_prompt import render_cli_command_prefix, shell_arg
from ...registry import registry_goals, resolve_state_file

Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/quota/monitor_poll.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from ..todos.external_wait_contract import (
build_monitor_advancement_authoring_contract,
)
from ..todos.projection import todo_item_task_class
from ..todos.todo_semantics import todo_item_task_class
from .decision_summary import compact_quota_decision, quota_decision_agent_id
from .spend_sources import DEFAULT_SLOT_SPEND_SOURCE

Expand Down
11 changes: 8 additions & 3 deletions loopx/control_plane/quota/monitor_poll_commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { access, readFile, rm } from "node:fs/promises";
import { basename, dirname, join, resolve } from "node:path";
import { monitorSuccessorIntent, monitorSuccessorRoute } from "../scheduler/monitor_successor.ts";
import { normalizeTodoCapabilities } from "../todos/work_requirements.ts";
import { parseProjectionDelivery } from "../todos/projection_delivery.ts";

import type { JsonObject } from "../effect_program.ts";
import { EffectRuntimeRequestError } from "../effect_runtime_errors.ts";
Expand Down Expand Up @@ -642,9 +643,13 @@ function compactProviderWriteback(receipt: JsonObject): JsonObject {
* Omitted on the legacy path to retain its exact v0 response shape. */
function monitorProjectionDelivery(receipt: JsonObject): JsonObject {
if (receipt.projection_delivery == null) return {};
const status = optionalString(receipt.projection_delivery, "projection_delivery");
if (!["delivered", "pending", "not_required"].includes(String(status))) {
throw new EffectRuntimeRequestError("invalid Monitor projection delivery status");
let status: ReturnType<typeof parseProjectionDelivery>;
try {
status = parseProjectionDelivery(receipt.projection_delivery);
} catch (error) {
throw new EffectRuntimeRequestError(
error instanceof Error ? error.message : "invalid Monitor projection delivery status",
);
}
const outbox = requiredObject(receipt.projection_outbox, "projection_outbox");
const diagnostic: JsonObject = {};
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/quota/projection_repair.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
TODO_TASK_CLASS_ADVANCEMENT,
normalize_required_write_scopes,
)
from ..todos.projection import (
from ..todos.todo_semantics import (
todo_item_is_actionable_open,
todo_item_task_class,
)
Expand Down
2 changes: 1 addition & 1 deletion loopx/control_plane/quota/should_run_packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
from ..todos.contract import (
normalize_todo_claimed_by,
)
from ..todos.projection import (
from ..todos.todo_semantics import (
todo_item_is_actionable_open as projection_todo_item_is_actionable_open,
)
from ..todos.quota_summary import (
Expand Down
10 changes: 1 addition & 9 deletions loopx/control_plane/quota/should_run_prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,11 @@
normalize_todo_resume_when,
normalize_todo_status,
)
from ..todos.projection import (
from ..todos.todo_semantics import (
todo_item_is_actionable_open as projection_todo_item_is_actionable_open,
)
from ..todos.projection import (
todo_item_is_due_monitor as projection_todo_item_is_due_monitor,
)
from ..todos.projection import (
todo_item_is_expired_monitor as projection_todo_item_is_expired_monitor,
)
from ..todos.projection import (
todo_item_next_due_at as projection_todo_item_next_due_at,
)
from ..todos.projection import (
todo_item_task_class as projection_todo_item_task_class,
)
from ..todos.quota_summary import (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
normalize_todo_task_domain,
normalize_todo_task_repository,
)
from ..todos.projection import todo_item_is_actionable_open
from ..todos.todo_semantics import todo_item_is_actionable_open
from ..work_items.primary_action import protocol_action_text
from .projection_repair import write_scope_allowed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Any

from ..todos.contract import normalize_todo_claimed_by, normalize_todo_id
from ..todos.projection import (
from ..todos.todo_semantics import (
todo_summary_claim_scope_agent_id,
todo_summary_has_only_future_scoped_monitor_work,
todo_summary_monitor_due_count,
Expand Down
Loading