Skip to content
Closed
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
35 changes: 15 additions & 20 deletions loopx/control_plane/heartbeat/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> "
Expand All @@ -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 `<current_time_iso>` 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; "
Expand Down
17 changes: 5 additions & 12 deletions loopx/control_plane/heartbeat/task_body.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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}
Expand All @@ -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 {}
Expand Down