Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
c92fb7f
refactor(shadow): retain observations independently of source producers
loopx-agent Oct 12, 2026
920e194
test(shadow): qualify retained observations and provider refusals
loopx-agent Oct 12, 2026
be6b032
docs(shadow): record retained observation exit and recovery
loopx-agent Oct 12, 2026
f2cf76b
Merge remote-tracking branch 'upstream/codex/c1-shadow-observation-ex…
Duang777 Oct 12, 2026
0de81f6
refactor(todos): retire legacy Markdown writers
Duang777 Oct 12, 2026
0217450
Merge remote-tracking branch 'upstream/main' into codex/remove-legacy…
Duang777 Oct 12, 2026
6192a0f
test(todos): migrate retired writer fixtures
Duang777 Oct 12, 2026
2560ecd
Merge remote-tracking branch 'upstream/main' into codex/remove-legacy…
Duang777 Oct 12, 2026
c04654a
test(todos): reject retired monitor writers
Duang777 Oct 12, 2026
e638ff4
test(todos): migrate remaining canonical fixtures
Duang777 Oct 12, 2026
179a54d
test(leases): retain only historical source cleanup
Duang777 Oct 12, 2026
9ffe9c0
test(todos): align retirement fixtures with canonical authority
Duang777 Oct 12, 2026
b9ec87c
test(chat): migrate team plan action fixtures
Duang777 Oct 12, 2026
11f3b2b
test(hosts): migrate release fixtures to canonical authority
Duang777 Oct 12, 2026
665da4d
fix(demo): initialize canonical todo authority
Duang777 Oct 12, 2026
478f32c
test(mcp): initialize canonical settlement fixtures
Duang777 Oct 12, 2026
4662fff
test(kiro): initialize canonical MCP fixtures
Duang777 Oct 12, 2026
3c06602
test(todos): retire legacy writer serialization coverage
Duang777 Oct 12, 2026
bb49ae1
test(team-plan): migrate settlement fixtures to authority
Duang777 Oct 12, 2026
6a7b754
test(explore): initialize canonical turn context fixture
Duang777 Oct 12, 2026
74a6a1b
fix(authority): align fail-closed qualification
Duang777 Oct 12, 2026
3c35f0f
test(authority): retire shadow fallback fixture
Duang777 Oct 12, 2026
1ac78d2
test(authority): migrate projected history fixtures
Duang777 Oct 12, 2026
d442126
test(authority): align recovery fixtures
Duang777 Oct 13, 2026
4993c7d
fix(authority): stabilize canonical verification
Duang777 Oct 13, 2026
fc59f3f
Merge upstream/main into codex/remove-legacy-markdown-callers-20261012
Duang777 Oct 14, 2026
8d7a885
test(authority): restore canonical monitor fixtures
Duang777 Oct 14, 2026
c44df7f
Merge upstream/main into codex/remove-legacy-markdown-callers-20261012
Duang777 Oct 14, 2026
b199d0b
fix(authority): commit imported untyped completion repairs
loopx-agent Oct 14, 2026
3fa639f
build: identify the canonical-only cutoff as 2.0 alpha
loopx-agent Oct 14, 2026
548b0f3
test(authority): promote browser fixtures before writes
Duang777 Oct 14, 2026
8880b20
fix(project): preserve canonical registration entry contracts
loopx-agent Oct 14, 2026
eebb6bf
ci: validate retained migration observations on Windows
loopx-agent Oct 14, 2026
80ee8b8
Merge latest contributor branch and retain migration coverage
loopx-agent Oct 14, 2026
e720933
fix(settings): await goal configuration readback
Duang777 Oct 14, 2026
ff39e67
Merge remote-tracking branch 'upstream/main' into codex/remove-legacy…
Duang777 Oct 14, 2026
25fd75a
Merge contributor main reconciliation and retire the static census
loopx-agent Oct 14, 2026
24e23ec
test(todos): retain delegated actor coverage on canonical providers
loopx-agent Oct 14, 2026
542bd80
test(app): restore disposable retirement receiver isolation
loopx-agent Oct 14, 2026
c486804
fix(team-plan): recover original receipts before fresh admission
loopx-agent Oct 14, 2026
fb13d8a
fix: require explicit repair for imported continuation policies
loopx-agent Oct 14, 2026
32c2a85
test: migrate promised Todo smokes to canonical transactions
loopx-agent Oct 14, 2026
dc084ba
fix: allow same-title successors in terminal transactions
loopx-agent Oct 14, 2026
ffab1e0
fix: replace complete folded Todo blocks during projection
loopx-agent Oct 14, 2026
81c81a6
test: migrate Todo CLI lifecycle coverage to canonical authority
loopx-agent Oct 14, 2026
aa30ea9
Merge pull request #1 from loopx-agent/codex/duang-retirement-recover…
Duang777 Oct 14, 2026
5f2b3b6
fix(authority): isolate imported completion repairs
Duang777 Oct 14, 2026
40f717a
test: qualify archive capacity validation and concurrent canonical wr…
loopx-agent Oct 14, 2026
cffc290
Merge remote-tracking branch 'refs/remotes/duang/codex/remove-legacy-…
loopx-agent Oct 14, 2026
d359b59
fix: admit readonly completion facts for imported metadata repair
loopx-agent Oct 14, 2026
422b017
docs: explain original-identity completion metadata repair
loopx-agent Oct 14, 2026
ec4632d
Merge remote-tracking branch 'upstream/main' into codex/pr6396-babysi…
Duang777 Oct 14, 2026
9e6d860
test: migrate user gate and monitor smokes to canonical authority
loopx-agent Oct 14, 2026
58cf90f
fix(authority): admit neutral imported repair envelopes
Duang777 Oct 14, 2026
5ec722d
fix(release): qualify prerelease candidate builds
Duang777 Oct 14, 2026
3fa2fd2
test(semantics): register direction authority consumer
Duang777 Oct 14, 2026
4979f0e
Merge remote-tracking branch 'upstream/main' into codex/pr6396-babysi…
Duang777 Oct 14, 2026
ddf8b45
fix: integrate Duang repair owner and preserve matching claim facts
loopx-agent Oct 14, 2026
6a600ee
docs: clarify authorized imported completion repair claims
loopx-agent Oct 14, 2026
876458e
Merge upstream/main into remove-legacy-markdown-callers
Duang777 Oct 14, 2026
89966f7
test: migrate status and quota smokes to canonical authority
loopx-agent Oct 14, 2026
e272560
chore: integrate current Duang retirement baseline
loopx-agent Oct 14, 2026
0cadb66
test: migrate peer workspace and lease lifecycle smokes to canonical
loopx-agent Oct 14, 2026
842003d
test: qualify canonical Chat actions and display recovery
loopx-agent Oct 14, 2026
7c57373
test: qualify canonical integrated advancement and role successors
loopx-agent Oct 14, 2026
e1304f5
fix: read maintainer correction ownership from canonical Todo
loopx-agent Oct 14, 2026
5dff847
test: migrate Explore and PR reconciliation callers to canonical
loopx-agent Oct 14, 2026
f63966e
test: qualify canonical project reconnect and adoption callers
loopx-agent Oct 14, 2026
d7a9885
fix: project governance leases from canonical authority
loopx-agent Oct 14, 2026
beecaea
test: migrate custom CLI and Lark gate callers to canonical
loopx-agent Oct 14, 2026
c9be156
refactor: retire isolated visible governance prototype
loopx-agent Oct 14, 2026
3df0992
fix: preserve canonical failure successor lineage and historical parents
loopx-agent Oct 14, 2026
58277fb
test: migrate dreaming and durable lifecycle callers to canonical aut…
loopx-agent Oct 14, 2026
e74a128
fix: recover cached material replan without repeating the Host
loopx-agent Oct 14, 2026
52fbb56
test: qualify explicit quota selection and routed user notices
loopx-agent Oct 14, 2026
a21153e
test: qualify project vision and next-step canonical callers
loopx-agent Oct 14, 2026
604482f
fix: classify existing CLI help and sync candidate manual version
loopx-agent Oct 14, 2026
20c6165
test: qualify public smoke prerequisites and structured contracts
loopx-agent Oct 14, 2026
131203f
fix: preserve settings readback in contributor integration
loopx-agent Oct 14, 2026
3b2f28c
test: restore canonical execution proof and retired-boundary coverage
loopx-agent Oct 14, 2026
f3ac697
fix: reject empty and late Host cleanup observations
loopx-agent Oct 15, 2026
c7b746f
fix(authority): recover original receipts before the writer cutoff
loopx-agent Oct 15, 2026
db445d6
fix(chat): keep the reviewed Todo operation identity
loopx-agent Oct 15, 2026
2af4fb3
test(authority): preserve canonical ownership and contention invariants
loopx-agent Oct 15, 2026
1353d40
docs(authority): reconcile retirement proposals and creation guidance
loopx-agent Oct 15, 2026
1c9547c
fix(authority): retire legacy execution policy and source lease grants
loopx-agent Oct 15, 2026
a4fed4f
docs(authority): reconcile two-policy retirement with the RFC
loopx-agent Oct 15, 2026
b9cc954
test(authority): use current policies in claim and deferral fixtures
loopx-agent Oct 15, 2026
8d7ba30
refactor(authority): retire unused source lease writers and admission…
loopx-agent Oct 15, 2026
d3f758b
docs(authority): record current policy and retired helper boundaries
loopx-agent Oct 15, 2026
7867467
test(authority): target retained release fence in mutation oracle
loopx-agent Oct 15, 2026
8cad0cb
test(authority): migrate remaining execution fixtures off legacy
loopx-agent Oct 15, 2026
dcdb730
test(authority): compare current-policy monitor recovery with full so…
loopx-agent Oct 15, 2026
d294f70
Merge upstream main into canonical retirement branch
loopx-agent Oct 15, 2026
2bb6a72
Merge updated canonical retirement base into supplement
loopx-agent Oct 15, 2026
7229251
Merge current main with the updated canonical retirement branch
songoow Oct 15, 2026
360686b
test(authority): align retirement regressions with canonical admission
songoow Oct 15, 2026
d9665e6
fix(settings): retain asynchronous readback callback types
songoow Oct 15, 2026
7534e38
Merge updated canonical retirement branch and preserve provider coverage
loopx-agent Oct 15, 2026
83b63ae
fix(ci): align full smoke qualification with current contracts
loopx-agent Oct 15, 2026
4aaec2d
Merge pull request #2 from loopx-project/codex/duang-canonical-valida…
huangruiteng Oct 15, 2026
b4c9e79
Merge remote-tracking branch 'origin/main' into codex/pr-6396-conflic…
songoow Oct 15, 2026
0e07abb
fix(monitor): recover original receipts before fresh authority admission
songoow Oct 15, 2026
a92f660
fix(leases): keep User actions outside execution lease authority
songoow Oct 15, 2026
f10ccea
fix(projects): replay registration without rebuilding canonical work
songoow Oct 15, 2026
afeb8a8
fix(team-plan): recover committed plans without readable display
songoow Oct 15, 2026
65c4a58
docs(authority): specify recovery and administrative action boundaries
songoow Oct 15, 2026
ec7ed65
Merge main into canonical retirement integration
loopx-agent Oct 15, 2026
00f24b0
test: qualify incoming Explore and status paths after canonical cutoff
loopx-agent Oct 15, 2026
05e143c
fix(monitor): preserve structured canonical admission refusals
songoow Oct 15, 2026
554da14
Merge contributor recovery fixes and preserve canonical fixture coverage
loopx-agent Oct 15, 2026
c0c202d
fix(leases): recover original source operations through the public CLI
songoow Oct 15, 2026
de455b7
test(authority): isolate fixtures and qualify current storage lease s…
songoow Oct 15, 2026
ffc2741
fix(delegation): retain the documented result-use verification budget
songoow Oct 15, 2026
60186f4
Merge contributor recovery fixes into canonical fixture supplement
loopx-agent Oct 15, 2026
dc633f3
test: distinguish fresh handoff routing from stale reconciliation
loopx-agent Oct 15, 2026
535a2ce
test: preserve failure risks across canonical status reconciliation
loopx-agent Oct 15, 2026
e3db550
fix(canary): replace retired status compaction smoke
loopx-agent Oct 15, 2026
5d29213
Merge pull request #3 from loopx-project/codex/duang-main-sync-20261015
huangruiteng Oct 15, 2026
819ea03
refactor(authority): share current execution target type
loopx-agent Oct 15, 2026
6683c74
docs(authority): reconcile retirement candidate and historical checkp…
loopx-agent Oct 15, 2026
8484a21
Merge pull request #4 from loopx-project/codex/duang-retirement-refin…
huangruiteng Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion .github/workflows/full-public-smokes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y zsh

- name: Install smoke runtime dependencies
run: python -m pip install --disable-pip-version-check "jsonschema>=4.23,<5"
# Smokes use the declared test extra (including YAML workflow readers).
# Install this source for qualification outside the checkout as well.
run: python -m pip install --disable-pip-version-check --editable '.[test]'

- name: Install locked TypeScript parser for semantic production checks
run: npm ci --ignore-scripts
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,7 @@ jobs:
tests/test_file_lock.py
tests/test_file_lock_cross_process.py
tests/control_plane/test_effect_runtime_integration.py
tests/control_plane/test_local_authority_shadow_outbox.py
tests/control_plane/test_shadow_observation_retirement.py
tests/test_self_update_runtime_activation.py
tests/test_windows_atomic_writes.py
tests/test_windows_install.py
Expand Down
4 changes: 2 additions & 2 deletions apps/presentation/dashboard/src/data/chat.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HANDOFF_MODES, EXECUTION_HANDOFF_MODES } from "../../../../../loopx/control_plane/coordination/handoff_mode_vocabulary.js";
import { HANDOFF_MODES, EXECUTION_HANDOFF_MODES, type ExecutionHandoffMode } from "../../../../../loopx/control_plane/coordination/handoff_mode_vocabulary.js";
import { normalizeGoalDraft } from "../../../../../loopx/control_plane/collaboration/goal_draft.js";
import { parseTurnStep, type TurnStep } from "./turn-steps.js";
import { z } from "zod";
Expand Down Expand Up @@ -2466,7 +2466,7 @@ export const ownershipPreviewSchema = z.object({
});
export type GoalOwnership = z.infer<typeof goalOwnershipSchema>;
export type OwnershipPreview = z.infer<typeof ownershipPreviewSchema>;
export type ExecutionOwnershipMode = typeof EXECUTION_HANDOFF_MODES[number];
export type ExecutionOwnershipMode = ExecutionHandoffMode;

export async function fetchGoalOwnership(goalId: string) {
return goalOwnershipSchema.parse(await requestJson<unknown>(`/api/chat/goal-ownership?${new URLSearchParams({goal_id: goalId})}`));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const capabilityCopy: Record<WorkspaceLocale, Record<string, LocalizedCopy>> = {
const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
en: {
new_goal_provider: { label: "New Goal storage provider", description: "File or SQLite; frozen at creation, independently of execution policy." },
canonical_creation: { label: "Create canonical authority", description: "Future Goals only. Disabled retains the post-promotion target; failures require the original creation retry." },
canonical_creation: { label: "Create canonical authority", description: "Required for new Goals in 2.0. An older disabled setting remains visible until you enable it; existing Goals require import." },
new_goal_handoff_mode: { label: "New Goal execution policy", description: "soft_claim or hard_lease, used with canonical creation. Agents inherit the Goal policy; tool authority is unchanged." },
runtime_profile: { label: "Runtime profile", description: "Restricted keeps scoped LoopX reads only. Trusted owner enables normal host tools while protected operations retain separate checks." },
selection_policy: { label: "Selection policy", description: "Preferred allows an explicit user choice; pinned rejects another executor; flexible permits fallback only inside the eligible pool." },
Expand Down Expand Up @@ -185,7 +185,7 @@ const fieldCopy: Record<WorkspaceLocale, FieldCopy> = {
},
"zh-CN": {
new_goal_provider: { label: "新 Goal 的存储 provider", description: "选择 File 或 SQLite;创建时固定,与执行策略分别选择。" },
canonical_creation: { label: "建立 canonical 权威存储", description: "仅影响此后新建的 Goal。关闭时仅固定晋升后的目标;失败须重试原创建操作。" },
canonical_creation: { label: "建立 canonical 权威存储", description: "2.0 新建 Goal 必须启用。旧的关闭设置会保留显示,需自行启用;已有 Goal 需导入。" },
new_goal_handoff_mode: { label: "新 Goal 的执行策略", description: "canonical 创建使用 soft_claim 或 hard_lease。Agent 继承 Goal 策略;不授予工具权限。" },
runtime_profile: { label: "运行模式", description: "restricted 仅使用受限 LoopX 读取;trusted_owner 开放常规宿主工具,但受保护操作仍单独校验。" },
selection_policy: { label: "选择策略", description: "preferred 允许用户显式改选;pinned 拒绝其他执行器;flexible 只在已授权资源池内回退。" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ function useCapabilityMutation({ goalId, loading, onApplied, selected, t }: Read

function CapabilityMutationFeedback({ mutationError, onApplied, partialWrite, preview }: Readonly<{
mutationError: string | null;
onApplied: () => void;
onApplied: () => Promise<void>;
partialWrite: GoalConfigurationPartialWrite | null;
preview: GoalConfigurationPreview | null;
}>) {
Expand All @@ -214,7 +214,7 @@ function CapabilityMutationFeedback({ mutationError, onApplied, partialWrite, pr
: "capabilities.partialWriteDescription")}</p>
<small>{partialWrite.recommended_action}</small>
</div>
<button onClick={onApplied} type="button"><RefreshCw aria-hidden size={15} />{t("capabilities.refreshSource")}</button>
<button onClick={() => void onApplied()} type="button"><RefreshCw aria-hidden size={15} />{t("capabilities.refreshSource")}</button>
</section>
) : null}
{preview ? (
Expand Down Expand Up @@ -304,7 +304,7 @@ function CapabilityCatalog({ callbacks, catalog, goalId, loading, notification,

{selected.capability_id === "progress_review" && selected.observation ? <section className="personal-capability-observation" aria-label={locale === "zh-CN" ? "审查证据读回" : "Review evidence readback"}>
<h3>{locale === "zh-CN" ? "审查证据读回" : "Review evidence readback"}</h3>
<button type="button" onClick={onApplied}>{locale === "zh-CN" ? "重新读取审查证据" : "Recheck review evidence"}</button>
<button type="button" onClick={() => void onApplied()}>{locale === "zh-CN" ? "重新读取审查证据" : "Recheck review evidence"}</button>
<p>{selected.observation.read_state === "unavailable" ? (locale === "zh-CN" ? "审查存储不可读,当前判断未知。" : "Review storage unavailable; current judgment unknown.")
: selected.observation.read_state === "missing" ? (locale === "zh-CN" ? "尚无审查回执。" : "No review receipts yet.")
: `${selected.observation.receipt_count} ${locale === "zh-CN" ? "条已读取回执" : "loaded receipts"}`}</p>
Expand Down Expand Up @@ -451,7 +451,7 @@ export function GoalCapabilitySettings({ callbacks, goalId, notification, onChan
<section className="personal-capability-error" role="alert">
<AlertTriangle aria-hidden size={18} />
<span><strong>{t("capabilities.loadFailed")}</strong><small>{error}</small></span>
<button onClick={load} type="button"><RefreshCw aria-hidden size={15} />{t("capabilities.retry")}</button>
<button onClick={() => void load()} type="button"><RefreshCw aria-hidden size={15} />{t("capabilities.retry")}</button>
</section>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ export function MachineConfigurationSettings({ section, onChanged }: { section:
<section className="personal-capability-behavior-note">
<ShieldCheck aria-hidden size={18} />
<div><strong>{locale === "zh-CN" ? "仅影响此后创建的 Goal" : "Future Goals only"}</strong><p>{locale === "zh-CN"
? "启用 canonical 创建后,新 Goal 直接建立权威存储,并采用所选执行策略;失败时须重试原创建操作。关闭时仅固定晋升后的目标存储。已有 Goal 的升级仍需备份、停止写入和结算租约;这里不授予工具权限。"
: "With canonical creation enabled, new Goals establish authority with the selected execution policy; retry the original operation after failure. Disabled only freezes the post-promotion target. Existing Goals still require backup, stopped writers and settled leases for upgrade; this setting grants no tool permissions."}</p></div>
? "2.0 新建 Goal 必须启用 canonical 创建,并采用所选执行策略。旧的关闭设置不会自动改变;启用后再重试创建。已有 Goal 需备份、停止写入并导入;这里不迁移已有数据,也不授予工具权限。"
: "2.0 requires canonical creation with the selected execution policy. An older disabled setting is not changed automatically; enable it before retrying creation. Back up, stop writers and import existing Goals separately. This setting neither migrates existing data nor grants tool permissions."}</p></div>
</section>
) : null}

Expand Down
4 changes: 4 additions & 0 deletions benchmark/tests/test_shared_codex_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ def test_scheduler_cancel_reaps_detached_worker_host_and_keeps_receipt(tmp_path)
str(source / "scripts/external_scheduler_worker.py"),
"--cli-bin",
str(cli),
"--registry",
str(tmp_path / "registry.json"),
"--runtime-root",
str(tmp_path / "runtime"),
"--goal-id",
"fixture",
"--agent-id",
Expand Down
27 changes: 25 additions & 2 deletions benchmark/tests/test_task_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,31 @@ def test_task_entry_defaults_to_planning_only_for_loopx_modes(mode):

@pytest.fixture
def planning_env(tmp_path):
from loopx.control_plane.coordination.runtime_shadow import (
build_todo_runtime_shadow_projection,
)
from tests.control_plane.canonical_authority_fixture import (
initialize_canonical_authority,
)

project = tmp_path / "project"
project.mkdir()
task = tmp_path / "task.md"
task.write_text("Repair the failure and preserve a regression test.\n")
state = tmp_path / "state.md"
state.write_text("# Active Goal State\n\n## Agent Todos\n\n## User Todos\n")
state.write_text(
"# Active Goal State\n\n"
"## User Todo / Owner Review Reading Queue\n\n"
"## Agent Todo\n\n"
"## Completed Work Archive\n"
)
runtime = tmp_path / "runtime"
registry = tmp_path / "registry.json"
registry.write_text(
json.dumps(
{
"schema_version": "0.1",
"common_runtime_root": str(runtime),
"goals": [
{
"id": "planning-goal",
Expand All @@ -64,6 +78,15 @@ def planning_env(tmp_path):
}
)
)
initialize_canonical_authority(
runtime,
"planning-goal",
build_todo_runtime_shadow_projection(
goal_id="planning-goal", todos=[], handoff_mode="soft_claim"
),
state_path=state,
provider="file",
)
cli = tmp_path / "loopx"
cli.write_text(
f"#!{sys.executable}\nfrom loopx.cli import main\nraise SystemExit(main())\n"
Expand Down Expand Up @@ -104,7 +127,7 @@ def planning_env(tmp_path):
"LOOPX_GOAL_ID": "planning-goal",
"LOOPX_AGENT_ID": "planner",
"LOOPX_REGISTRY": str(registry),
"LOOPX_RUNTIME_ROOT": str(tmp_path / "runtime"),
"LOOPX_RUNTIME_ROOT": str(runtime),
"LOOPX_CLI": str(cli),
"CODEX_BIN": str(binary),
"LOOPX_PROJECT": str(project),
Expand Down
53 changes: 33 additions & 20 deletions demo/auto_research/worker_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
normalize_todo_id,
)
from loopx.control_plane.todos.handoff_mode import HANDOFF_MODE_HARD_LEASE
from loopx.control_plane.coordination.local_authority import (
LocalCoordinationAuthorityUnavailable, read_canonical_todos_if_promoted,
)
from loopx.control_plane.work_items.task_lease import (
TaskLeaseError,
acquire_task_lease,
Expand Down Expand Up @@ -388,28 +391,38 @@ def _maybe_add_role_successor_todos(
action=action,
)
failure_continuation = decision_summary.get("failure_continuation")
if isinstance(failure_continuation, dict):
next_outcome = str(failure_continuation.get("next_outcome") or "")
successor_specs = [
spec
for spec in successor_specs
if spec.get("action_kind") == next_outcome
]
text_source_todo_id = None
if isinstance(failure_continuation, dict):
text_source_todo_id = str(failure_continuation.get("source_todo_id") or "") or None
return apply_role_successor_todos(
registry_path=registry_path,
goal_id=goal_id,
source_todo_id=source_todo_id,
current_agent_id=agent_id,
role_id=role_id,
action=action,
successor_specs=successor_specs,
decision_summary=decision_summary,
execute=execute,
text_source_todo_id=text_source_todo_id,
)
next_outcome = str(failure_continuation.get("next_outcome") or "")
successor_specs = [spec for spec in successor_specs if spec.get("action_kind") == next_outcome]
source_todo_id = str(failure_continuation.get("source_todo_id") or "") or source_todo_id
text_source_todo_id = source_todo_id
def apply_binding(parent_id: str, *, revision: str | None = None) -> dict[str, object]:
return apply_role_successor_todos(registry_path=registry_path, goal_id=goal_id,
source_todo_id=parent_id, current_agent_id=agent_id, role_id=role_id,
action=action, successor_specs=successor_specs, decision_summary=decision_summary,
execute=execute, text_source_todo_id=text_source_todo_id,
expected_provider_revision=revision)

try:
return apply_binding(source_todo_id)
except LocalCoordinationAuthorityUnavailable as error:
if error.code != 'todo_semantic_duplicate_conflict' or text_source_todo_id is None:
raise
# Native create owns normalized title/intent matching. Qualify only its
# exact conflicting record, without rewriting a historical parent.
snapshot = read_canonical_todos_if_promoted(
runtime_root=runtime_root_from_registry(registry_path, None), goal_id=goal_id,
)
todos = {row['todo_id']: row for row in (snapshot or {}).get('todos', [])}
existing = todos.get(error.payload.get('todo_id'))
parent_id = existing.get('unblocks_todo_id') if existing else None
parent = todos.get(parent_id)
if not (parent and parent['role'] == 'agent' and parent.get('action_kind') in SUMMARY_ACTIONS
and parent.get('unblocks_todo_id') == text_source_todo_id):
raise
return apply_binding(parent_id, revision=snapshot['provider_revision'])



def _executed_successor_todo_ids(successor_todos: dict[str, object]) -> list[str]:
Expand Down
11 changes: 6 additions & 5 deletions demo/multi_agent/role_successor.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,13 @@ def apply_role_successor_todos(
decision_summary: Mapping[str, object],
execute: bool,
text_source_todo_id: str | None = None,
expected_provider_revision: str | None = None,
) -> dict[str, object]:
"""Preview or create role-declared successor todos through LoopX state.
"""Create successors, retaining qualified historical source bindings.

When text_source_todo_id is provided, it is used for rendering the
successor text (so the title is stable across different summary todos
that review the same failure lineage). source_todo_id is still used
for the unblocks_todo_id binding.
Normally one source supplies both title and lineage. A caller recovering
an existing successor may preserve its original parent while rendering
the same title from the independently verified evidence lineage.
"""

specs = [dict(spec) for spec in successor_specs if isinstance(spec, Mapping)]
Expand Down Expand Up @@ -257,6 +257,7 @@ def apply_role_successor_todos(
action_kind=action_kind,
claimed_by=claimed_by,
unblocks_todo_id=source_todo_id,
expected_provider_revision=expected_provider_revision,
dry_run=False,
)
successors.append(
Expand Down
Loading
Loading