diff --git a/loopx/control_plane/heartbeat/rules.py b/loopx/control_plane/heartbeat/rules.py index f994fc13ab..ef43086108 100644 --- a/loopx/control_plane/heartbeat/rules.py +++ b/loopx/control_plane/heartbeat/rules.py @@ -15,21 +15,19 @@ "the projection internally and stay quiet." ) HEARTBEAT_NOTIFICATION_RULE_SHORT = ( - "`user_channel.notify` controls OUTPUT only: NOTIFY=向用户输出动作; " - "DONT_NOTIFY=安静输出。见 `heartbeat_recommendation.agent_must_attempt`/" - "`execution_obligation.must_attempt_work`:true须推进并写回,false才可no-op。" - "Due/peer非用户动作;NOTIFY缺动作→" + "DONT_NOTIFY is OUTPUT only; execution follows " + "`heartbeat_recommendation.agent_must_attempt`/" + "`execution_obligation.must_attempt_work`. NOTIFY缺动作→" "具体user todo未投影,需修复LoopX状态投影;静默时内部修复。" ) HEARTBEAT_NOTIFICATION_RULE_THIN = ( - "`user_channel.notify` controls OUTPUT only: NOTIFY=向用户输出动作; " - "DONT_NOTIFY=安静输出。执行义务看 `agent_must_attempt`/`must_attempt_work`。" - "Due/peer非用户动作;NOTIFY缺动作→" + "DONT_NOTIFY is OUTPUT only; execution follows `agent_must_attempt`/" + "`execution_obligation.must_attempt_work`. NOTIFY缺动作→" "具体user todo未投影,需修复LoopX状态投影;静默时内部修复。" ) HEARTBEAT_VISION_WRITEBACK_RULE_SHORT = ( - "writeback: no-change=`surface_only`/no spend; " - "unchanged->`--vision-unchanged-reason`; material->actual outcome." + "Vision: no-change=`surface_only`/no spend + `--vision-unchanged-reason`; " + "material=actual outcome." ) SCHEDULER_HINT_APPLICATION_RULE = ( "`scheduler_hint` no-spend. host_action=pause_or_delete_current_heartbeat -> " @@ -44,27 +42,24 @@ "then ack/fail. No spend." ) SCHEDULER_HINT_THIN_RULE = ( - "host_action=pause_or_delete_current_heartbeat->automation_update stop(no-spend); " - "else RRULE/fallback_hint/ack/fail." + "host_action=pause_or_delete_current_heartbeat->automation_update; " + "else RRULE/fallback_hint/ack/fail; no spend." ) RUNTIME_CAPABILITY_PROJECTION_THIN_RULE = ( - "Observed capabilities -> `--available-capability`; never user gates." + "`--available-capability`; not user gates." ) RUNTIME_REPAIR_ROUTING_RULE = ( - "use `loopx-project` for " - "lifecycle/registry and `loopx-self-repair` for runtime/projection drift." + "Repair: `loopx-project`/`loopx-self-repair`." ) RUNTIME_EXECUTION_ROUTING_RULE = ( - "Normal turns use CLI `interaction_contract`; " + RUNTIME_REPAIR_ROUTING_RULE + "Current CLI `interaction_contract`; " + RUNTIME_REPAIR_ROUTING_RULE ) HOST_LOOP_SAFETY_RULE = ( - "Follow user authority and repository rules. Protect credentials/private material; " - "publish public-safe evidence. Destructive Git/production requires explicit authorization. " - "Gate only the affected path; continue independent allowed work." + "User/repository rules; protect credentials/private material. " + "Destructive Git/production needs authorization. Gate only the affected path." ) HEARTBEAT_TURN_BOOTSTRAP_RULE = ( - "Per wake, replace `` once. Run assignment and guard as separate " - "statements in one shell, not a command-prefix assignment; reuse the value on retries." + "Per wake set/reuse `LOOPX_TURN`; run assignment, then the complete guard." ) HOST_LOOP_QUOTA_DISPATCH_RULE = ( "Quota: use selection_command when required; " diff --git a/loopx/control_plane/heartbeat/task_body.py b/loopx/control_plane/heartbeat/task_body.py index ce2583ae67..7a2279b674 100644 --- a/loopx/control_plane/heartbeat/task_body.py +++ b/loopx/control_plane/heartbeat/task_body.py @@ -325,12 +325,9 @@ def render_brief_heartbeat_task_body( Post-spend state: `{refresh_state_command}` -No spend for quiet skips, preflight failures, blocker-push asks, dry-runs, or -duplicate accounting. Return only under `user_channel.notify=NOTIFY`; else quiet. - -{HOST_LOOP_SAFETY_RULE} -{RUNTIME_REPAIR_ROUTING_RULE} +No spend for skips/failures/dry-runs/duplicates; output only under `NOTIFY`. +{HOST_LOOP_SAFETY_RULE} {RUNTIME_REPAIR_ROUTING_RULE} {material_queue_rule} {permission_rule}""" def render_compact_heartbeat_task_body( @@ -649,8 +646,7 @@ def render_thin_heartbeat_task_body( ) return f"""Advance `{goal_id}` from {active_state}. -{RUNTIME_EXECUTION_ROUTING_RULE} -{HOST_LOOP_SAFETY_RULE} +{RUNTIME_EXECUTION_ROUTING_RULE} {HOST_LOOP_SAFETY_RULE} {scope_sentence} {HOST_LOOP_QUOTA_DISPATCH_RULE} @@ -664,11 +660,8 @@ def render_thin_heartbeat_task_body( {RUNTIME_CAPABILITY_PROJECTION_THIN_RULE} {SCHEDULER_HINT_THIN_RULE} {HEARTBEAT_VISION_WRITEBACK_RULE_SHORT} -Done->todo/rationale; guard receipt; 2 stalls->replan. -`agent_read_required`: drain/read/triage before work; settle/ACK. - -P0 blocked: safe P1/P2; monitor quiet/no-spend. - +`agent_read_required`: read/settle/ACK; 2 stalls replan; P0 blocked->P1/P2; +monitor quiet/no-spend. {policy_tail}""" def render_heartbeat_generator_inputs_markdown(payload: dict[str, Any]) -> str: interface_budget = payload.get("interface_budget") if isinstance(payload.get("interface_budget"), dict) else {}