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: 15 additions & 1 deletion benchmark/tests/test_native_checkpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ def test_real_native_interrupt_retains_branch_but_does_not_qualify_checkpoint(tm
boundary; a successful native fork alone must not loosen that contract.
"""
import subprocess
from pathlib import Path
from loopx.capabilities.benchmark_toolkit.native_codex_goal import StdioNativeGoalTransport

workspace = tmp_path / "work"
Expand Down Expand Up @@ -261,6 +262,9 @@ def wait_terminal(native, session, turn, status):
with pytest.raises(ValueError, match="completed final cursor"):
native_session_checkpoint(env, source, timeout=15)
with native_host(env) as native:
source_rollouts = list((Path(env["CODEX_HOME"]) / "sessions").rglob(f"*{source}.jsonl"))
assert len(source_rollouts) == 1
original_rollout = source_rollouts[0].read_bytes()
fork = native.request("thread/fork", {"threadId": source, "cwd": str(workspace),
"lastTurnId": turn, "persistExtendedHistory": True})["thread"]["id"]
assert fork != source and history(native, fork) == retained
Expand All @@ -269,7 +273,17 @@ def wait_terminal(native, session, turn, status):
wait_terminal(native, fork, next_turn, "completed")
assert (workspace / "continued.txt").read_text() == "continued-candidate\n"
assert "retained-candidate" in json.dumps(requests[2])
assert history(native, source) == retained
assert len(requests) == 4
# Extended history may hydrate the interrupted command's terminal
# metadata later. The persisted original and all model input must
# remain unchanged; that derived metadata is not a new source Turn.
assert source_rollouts[0].read_bytes() == original_rollout
original = history(native, source)
assert [row["id"] for row in original] == [row["id"] for row in retained]
assert original[:-1] == retained[:-1]
assert original[-1]["status"] == retained[-1]["status"] == "interrupted"
assert [item for item in original[-1]["items"] if item["type"] == "userMessage"] == [
item for item in retained[-1]["items"] if item["type"] == "userMessage"]


@pytest.mark.skipif(not os.environ.get("LOOPX_NATIVE_CHECKPOINT_BIN"),
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/rfcs/STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ appendix may keep dated history, but no dated log heading may precede it.
| [RFC: Shared Goal Alignment and Governed Amendment Protocol (v0)](shared-goal-alignment-and-governed-amendment-v0.md) | Accepted | none | [2 entries](ledger/shared-goal-alignment-and-governed-amendment-v0/) |
| [RFC: LoopX Shared Control-Plane Authority and Pluggable State Providers (v0)](shared-goal-authority-state-provider-v0.md) | Accepted | none | [24 entries](ledger/shared-goal-authority-state-provider-v0/) |
| [RFC: Single-Owner Local Daemon (v0)](single-owner-local-daemon-v0.md) | Accepted | none | — |
| [RFC: TypeScript Control-Plane Migration Direction v0](typescript-control-plane-migration-v0.md) | Accepted | none | [16 entries](ledger/typescript-control-plane-migration-v0/) |
| [RFC: TypeScript Control-Plane Migration Direction v0](typescript-control-plane-migration-v0.md) | Accepted | none | [18 entries](ledger/typescript-control-plane-migration-v0/) |

## Superseded (1)

Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/rfcs/STATUS.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
| [RFC:共享 Goal 对齐与受治理 Amendment 协议(v0)](shared-goal-alignment-and-governed-amendment-v0.zh-CN.md) | 已接受 | 无 | [2 条](ledger/shared-goal-alignment-and-governed-amendment-v0/) |
| [RFC:LoopX 共享控制面权威与可插拔状态 Provider(v0)](shared-goal-authority-state-provider-v0.zh-CN.md) | 已接受 | 无 | [24 条](ledger/shared-goal-authority-state-provider-v0/) |
| [RFC: Single-Owner Local Daemon (v0)](single-owner-local-daemon-v0.md) | 已接受 | none | — |
| [RFC:LoopX 控制面 TypeScript 渐进迁移方向 v0](typescript-control-plane-migration-v0.zh-CN.md) | 已接受 | 无 | [16 条](ledger/typescript-control-plane-migration-v0/) |
| [RFC:LoopX 控制面 TypeScript 渐进迁移方向 v0](typescript-control-plane-migration-v0.zh-CN.md) | 已接受 | 无 | [18 条](ledger/typescript-control-plane-migration-v0/) |

## 已被替代 (1)

Expand Down
3 changes: 2 additions & 1 deletion loopx/control_plane/todos/context_projection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { requireBoolean, requireInteger, requireJsonObject } from "../runtime_de
import { canonicalAuthorityJson } from "../coordination/authority_store_codec.ts";
import {compactThinTodoRead, compactTodoReadSummary, compactThinTodoReadSummary} from "./read_presentation.ts";
import {TODO_DECISION_SCOPE_OUTCOME_SCHEMA_VERSION} from "./user_completion_types.ts";
import {removedTodoContinuationPolicy} from "./legacy_continuation_policy_migration.ts";

// A read lens over canonical records, shared by global and Goal conversations.
// These are existing Todo facts, not dependency evaluation or execution grants.
Expand Down Expand Up @@ -222,7 +223,7 @@ function compactSummaryRecord(item: JsonObject, body: unknown): JsonObject {
decision_scope: readScope(row.decision_scope), required_decision_scopes: readScopes(row.required_decision_scopes),
decision_outcome: outcome(token("decision_outcome")), decision_scope_outcomes: outcomes,
excluded_agents: readExcludedAgents(row.excluded_agents),
removed_continuation_policy: ["primary_review", "review_handoff"].includes(token("removed_continuation_policy").toLowerCase()) ? token("removed_continuation_policy").toLowerCase() : null,
removed_continuation_policy: removedTodoContinuationPolicy(token("removed_continuation_policy")),
replan_obligation_id: /^replan-[a-f0-9]{16}$/.test(token("replan_obligation_id")) ? token("replan_obligation_id") : null};
for (const [key, value] of Object.entries(normalized)) {
if (value !== null && (!Array.isArray(value) || value.length)) row[key] = value; else delete row[key];
Expand Down
10 changes: 7 additions & 3 deletions tests/control_plane_ts/authority_store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,16 @@ test("concurrent cold reads share only the same exact-byte proof and recover aft
const corrupt = JSON.parse(valid); corrupt.committed[0].provider_revision = "corrupt";
await writeFile(store.path, JSON.stringify(corrupt));
assert.ok((await Promise.all(readers.map(s => s.loadAuthority()))).every(r => r.status === "failed"));
assert.equal(CountingStore.validations, 2);
// Only overlapping proofs coalesce. A slower file read may enter after a
// rejection has already been evicted and must then validate again.
const rejectedValidations = CountingStore.validations;
assert.ok(rejectedValidations > 1, "changed corrupt bytes require a new proof");
assert.equal((await readers[0]!.loadAuthority()).status, "failed");
assert.equal(CountingStore.validations, 3, "a rejected promise must not remain in the in-flight registry");
assert.equal(CountingStore.validations, rejectedValidations + 1,
"a rejected promise must not remain in the in-flight registry");
await writeFile(store.path, valid + "\n\n");
assert.equal((await readers[0]!.loadAuthority()).status, "loaded");
assert.equal(CountingStore.validations, 4);
assert.equal(CountingStore.validations, rejectedValidations + 2);
});

test("alternating File stores reuse their own exact-byte proofs across handles", async t => {
Expand Down
Loading