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
40 changes: 25 additions & 15 deletions docs/reference/protocols/goal-vision-replan-contract-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,24 +107,26 @@ other's active vision.

### Path Delta

A machine-generated vision packet may include one optional
`goal_path_delta_v0`. It makes a bounded loop's look-back explicit without
adding more inline CLI flags or expanding the heartbeat prompt. The packet is
written through the existing `--agent-vision-json` boundary and is retained in
the same agent-scoped run-history and shared-runtime vision projection:
A vision packet may include a top-level `path_delta` object; `goal_path_delta_v0`
is its `schema_version`, not its enclosing field. The shared TypeScript authoring
boundary rejects misplaced declared deltas before any write, including through
CLI and Turn. It does not infer a protocol from ordinary metadata field names.
Existing packets may omit the nested schema version; an explicitly supplied
version must match. Historical read compaction remains unchanged.

```json
{
"schema_version": "goal_path_delta_v0",
"outcome": "replan",
"prior_assumption": "The current monitor lane would produce acceptance evidence.",
"observed_reality": "Two bounded polls produced no material transition.",
"retained": ["Keep the verified monitor target and evidence refs."],
"changed": ["Create one runnable advancement successor."],
"stopped": ["Stop treating future polling as completion evidence."],
"unresolved_questions": ["Which successor can falsify the new path?"],
"reentry_condition": "Resume the monitor-only wait after successor evidence lands.",
"evidence_refs": ["evidence:monitor-poll-02", "todo:successor-01"]
"vision_patch": {"vision_summary": "Deliver the verified successor."},
"path_delta": {
"schema_version": "goal_path_delta_v0",
"outcome": "replan",
"prior_assumption": "Polling would produce acceptance evidence.",
"observed_reality": "Repeated polls produced no material transition.",
"retained": ["Keep the verified monitor target."],
"changed": ["Create one runnable advancement successor."],
"stopped": ["Stop treating polling as completion evidence."],
"evidence_refs": ["evidence:monitor-poll", "todo:successor"]
}
}
```

Expand Down Expand Up @@ -232,6 +234,14 @@ Valid checkpoint decisions are:
- `not_required`: no material closeout trigger was present, including a valid
typed in-flight continuation.

A material closeout should carry its own vision patch or evidence-backed unchanged
reason. If omitted, `refresh-state` still records the outcome and returns the
checkpoint repair action. Follow that action in the same turn with the original
settlement identity, removing already executed state mutations. The supplement
must satisfy the checkpoint before terminal closeout; it neither re-authors the
outcome nor spends a second time. Never invent an unchanged reason to clear a gap.
Typed in-flight continuations keep their existing exemption.

`missing_required` is not a chat reminder. Status keeps it in compact run
history, quota filters it by current `agent_id`, and goal-frontier projection
turns it into `acceptance_gaps[]`. If the current agent has no runnable
Expand Down
12 changes: 7 additions & 5 deletions examples/control_plane/heartbeat-prompt-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,10 @@ def user_output_policy(task_body: str, *, mode: str) -> dict[str, str]:

def assert_sole_notification_authority(task_body: str, *, mode: str) -> None:
body = normalized(task_body)
assert "no-change=`surface_only`/no spend; unchanged->" in body, mode
assert "`--vision-unchanged-reason`; material->actual outcome." in body, mode
assert "no-change=`surface_only`/no spend" in body, mode
assert "material=实际outcome+vision决定" in body, mode
assert "缺则同turn按返回命令补齐再terminal" in body, mode
assert "unchanged→真实`--vision-unchanged-reason`" in body, mode

if mode == "full":
assert (
Expand Down Expand Up @@ -596,7 +598,7 @@ def main() -> int:
"host_action=pause_or_delete_current_heartbeat->automation_update stop(no-spend)",
"else RRULE/projected-fallback_hint/ack/fail",
"no-change=`surface_only`/no spend",
"unchanged->`--vision-unchanged-reason`",
"unchanged→真实`--vision-unchanged-reason`",
"guard; 2 stalls->replan",
"`agent_read_required`",
"drain/read/triage before work; settle/ACK",
Expand Down Expand Up @@ -698,7 +700,7 @@ def main() -> int:
"host_action=pause_or_delete_current_heartbeat->automation_update stop(no-spend)",
"else RRULE/projected-fallback_hint/ack/fail",
"no-change=`surface_only`/no spend",
"unchanged->`--vision-unchanged-reason`",
"unchanged→真实`--vision-unchanged-reason`",
"guard; 2 stalls->replan",
"P0 blocked: safe P1/P2",
"monitor quiet/no-spend",
Expand All @@ -714,7 +716,7 @@ def main() -> int:
):
assert "no-change=`surface_only`/no spend" in task, label
assert "`--vision-unchanged-reason`" in task, label
assert "material->actual outcome" in task, label
assert "material=实际outcome+vision决定" in task, label
assert "if absent say" not in thin_task, thin_task
assert "If false/0: quiet/no-user-todo" not in thin_task, thin_task

Expand Down
4 changes: 2 additions & 2 deletions examples/project/goal-vision-refresh-state-budget-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ def main() -> int:
check=False,
)
assert unexplained_inline_drift.returncode == 1, unexplained_inline_drift
assert "provide goal_path_delta_v0 with outcome=replan" in payload(
assert "provide path_delta with schema_version=goal_path_delta_v0 and outcome=replan" in payload(
unexplained_inline_drift
)["error"], unexplained_inline_drift.stdout

Expand All @@ -391,7 +391,7 @@ def main() -> int:
check=False,
)
assert unexplained_drift.returncode == 1, unexplained_drift
assert "provide goal_path_delta_v0 with outcome=replan" in payload(
assert "provide path_delta with schema_version=goal_path_delta_v0 and outcome=replan" in payload(
unexplained_drift
)["error"], unexplained_drift.stdout

Expand Down
25 changes: 23 additions & 2 deletions loopx/control_plane/goals/vision_checkpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
const PRIVATE_TEXT_PATTERNS = [
/\/Users\//,
/\/ext_data\//,
/larkoffice/i,
/lark[o]ffice/i, // Equivalent matcher avoids matching its own policy source.

Check warning on line 118 in loopx/control_plane/goals/vision_checkpoint.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace this character class by the character itself.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaCu60sVQlmyP3S-okj8&open=AaCu60sVQlmyP3S-okj8&pullRequest=4637
/docs\.internal/i,
/\bt-20\d{12}-[a-z0-9]+\b/,
/\bBearer\b/i,
Expand Down Expand Up @@ -340,6 +340,11 @@
): [JsonObject | null, Record<string, number>] {
if (value === null || value === undefined) return [null, {}];
const source = requiredObject(value, "agent_vision.path_delta");
if (source.schema_version !== undefined && source.schema_version !== GOAL_PATH_DELTA_SCHEMA_VERSION) {
throw new EffectRuntimeRequestError(
`agent_vision.path_delta.schema_version must be ${GOAL_PATH_DELTA_SCHEMA_VERSION}`,
);
}
const outcome = compactText(source.outcome).toLowerCase().replaceAll("-", "_");
if (!(GOAL_PATH_DELTA_OUTCOMES as readonly string[]).includes(outcome)) {
throw new EffectRuntimeRequestError(
Expand Down Expand Up @@ -484,6 +489,22 @@

function prepareVisionRefresh(request: VisionRefreshPrepareRequest): JsonObject {
const packet = request.agent_vision_packet;
// Validate authoring before merge/compaction can silently discard a declared
// protocol. Ordinary extension metadata is not classified by overlapping keys.
for (const [container, prefix] of [[packet, "agent_vision"], [packet.vision_patch, "agent_vision.vision_patch"]] as const) {
if (typeof container !== "object" || container === null || Array.isArray(container)) continue;
for (const [field, value] of Object.entries(container)) {
if (prefix === "agent_vision" && field === "path_delta") continue;
if (field === GOAL_PATH_DELTA_SCHEMA_VERSION ||
(prefix === "agent_vision.vision_patch" && field === "path_delta") ||
(typeof value === "object" && value !== null && !Array.isArray(value) &&
(value as JsonObject).schema_version === GOAL_PATH_DELTA_SCHEMA_VERSION)) {
throw new EffectRuntimeRequestError(
`${prefix}.${field} must be supplied as agent_vision.path_delta; ${GOAL_PATH_DELTA_SCHEMA_VERSION} is the schema_version, not the enclosing field`,
);
}
}
}
const existing = request.existing_agent_vision ?? {};
const updatePacket: JsonObject = { ...packet };
if (request.merge_patch && Object.keys(existing).length > 0) {
Expand Down Expand Up @@ -619,7 +640,7 @@
);
if (changedFields.length > 0 && pathDelta?.outcome !== "replan") {
throw new EffectRuntimeRequestError(
`autonomous agent vision replan changes durable fields ${changedFields.join(", ")}; provide goal_path_delta_v0 with outcome=replan so the mainline change is explicit`,
`autonomous agent vision replan changes durable fields ${changedFields.join(", ")}; provide path_delta with schema_version=goal_path_delta_v0 and outcome=replan so the mainline change is explicit`,
);
}
}
Expand Down
4 changes: 2 additions & 2 deletions loopx/control_plane/heartbeat/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
"具体user todo未投影,需修复LoopX状态投影;静默时内部修复。"
)
HEARTBEAT_VISION_WRITEBACK_RULE_SHORT = (
"writeback: no-change=`surface_only`/no spend; "
"unchanged->`--vision-unchanged-reason`; material->actual outcome."
"writeback: no-change=`surface_only`/no spend;material=实际outcome+vision决定;"
"缺则同turn按返回命令补齐再terminal;unchanged→真实`--vision-unchanged-reason`。"
)
REWARD_MEMORY_OUTCOME_RULE = (
"`reward_memory_recall.experiment.automatic_ingest=true`: reusable Todo outcomes "
Expand Down
31 changes: 31 additions & 0 deletions tests/control_plane/test_vision_budget_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,34 @@ def test_full_budget_roundtrips_without_erasing_replan_or_partial_writes(tmp_pat
assert "total_agent_vision uses 1801 chars; limit is 1800" in fixture.payload(rejected)["error"]
assert index.read_bytes() == before_index
assert state.read_bytes() == before_state


def test_misplaced_delta_rejects_before_write_and_corrected_packet_roundtrips(tmp_path):
source = Path(__file__).resolve().parents[2] / "examples/project/goal-vision-refresh-state-budget-smoke.py"
spec = importlib.util.spec_from_file_location("vision_packet_fixture", source)
fixture = importlib.util.module_from_spec(spec)
spec.loader.exec_module(fixture)
registry, runtime, project = fixture.write_fixture(tmp_path)
state = project / ".codex/goals" / fixture.GOAL_ID / "ACTIVE_GOAL_STATE.md"
before = state.read_bytes()
delta = {"schema_version": "goal_path_delta_v0", "outcome": "replan",
"prior_assumption": "Keep the route.", "observed_reality": "A dependency changed.",
"changed": ["Use the successor."]}
packet = {"vision_patch": {"vision_summary": "Deliver the successor."},
"goal_path_delta_v0": delta}
path = tmp_path / "vision.json"
fixture.write_json(path, packet)
rejected = fixture.run_cli(registry, runtime, vision_path=path, check=False,
dry_run=False, autonomous_replan_recorded=False)
assert rejected.returncode == 1
assert "must be supplied as agent_vision.path_delta" in fixture.payload(rejected)["error"]
assert state.read_bytes() == before
index = runtime / "goals" / fixture.GOAL_ID / "runs/index.jsonl"
assert not index.exists()
packet["path_delta"] = packet.pop("goal_path_delta_v0")
packet["telemetry"] = {"outcome": "ok", "evidence_refs": ["evidence:probe"]}
fixture.write_json(path, packet)
result = fixture.payload(fixture.run_cli(registry, runtime, vision_path=path, check=True,
dry_run=False, autonomous_replan_recorded=False))
assert result["agent_vision"]["path_delta"] == delta
assert json.loads(index.read_text().splitlines()[-1])["agent_vision"]["path_delta"] == delta
30 changes: 29 additions & 1 deletion tests/control_plane_ts/vision_checkpoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,34 @@ test("prepare owns packet normalization, budgets, and path delta", () => {
});
});

test("authoring rejects misplaced declared deltas without classifying telemetry", () => {
const delta = {schema_version: "goal_path_delta_v0", outcome: "replan",
prior_assumption: "Keep the route.", observed_reality: "A dependency changed.",
changed: ["Use the successor."]};
const patch = {vision_summary: "Deliver the successor."};
for (const extra of [
{goal_path_delta_v0: delta}, {comparison: delta},
{path_delta: delta, comparison: delta},
{vision_patch: {...patch, path_delta: delta}},
]) {
assert.throws(() => buildVisionCheckpoint(prepareRequest({
agent_vision_packet: {vision_patch: patch, ...extra},
})), /must be supplied as agent_vision.path_delta/);
}
const telemetry = {outcome: "ok", evidence_refs: ["evidence:probe"]};
const baseline = buildVisionCheckpoint(prepareRequest({agent_vision_packet: {vision_patch: patch}}));
assert.deepEqual(buildVisionCheckpoint(prepareRequest({
agent_vision_packet: {vision_patch: patch, telemetry},
})), baseline);
assert.throws(() => buildVisionCheckpoint(prepareRequest({agent_vision_packet: {
vision_patch: patch, path_delta: {...delta, schema_version: "unsupported"},
}})), /path_delta.schema_version must be goal_path_delta_v0/);
const accepted = buildVisionCheckpoint(prepareRequest({agent_vision_packet: {
vision_patch: patch, path_delta: delta, telemetry,
}}));
assert.deepEqual((accepted.agent_vision as Record<string, unknown>).path_delta, delta);
});

test("structured replans have a bounded 1800-character budget including path evidence", () => {
for (const character of ["x", "界"]) {
// Independent boundary oracle: 420 + 420 + 280 + 320 + 320 + 6 + 34.
Expand Down Expand Up @@ -183,7 +211,7 @@ test("prepare merges a patch and requires an explicit durable replan", () => {
merge_patch: true,
require_path_delta_for_durable_change: true,
})),
/provide goal_path_delta_v0 with outcome=replan/,
/provide path_delta with schema_version=goal_path_delta_v0 and outcome=replan/,
);
});

Expand Down