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
8 changes: 5 additions & 3 deletions docs/heartbeat-automation-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -727,9 +727,11 @@ For every automatic heartbeat turn, the agent-facing checklist is:
Every heartbeat guard commits one idempotent receipt. If
`effective_action=monitor_quiet_skip`, it also commits the no-spend stall
observation and returns the follow-up decision; do not append another manual
poll. Return quiet `DONT_NOTIFY` if it remains monitor-only. Keep monitor
todos visible but do no delivery edits and no spend until material evidence
changes or the guard exposes `autonomous_replan_required` /
poll. For a monitor-only turn with `DONT_NOTIFY`, leave chat quiet. If the
host requires a completion result, use its complete quiet result without
additional chat text. A bare decision or skip explanation is not a host
result. Keep monitor todos visible and do no delivery edits or spend until
material evidence changes or the guard exposes `autonomous_replan_required` /
`execution_obligation.must_attempt_work=true`.
3. For `notify_user_on_open_todo=true`, follow the current
`interaction_contract.user_channel` for the concrete notice and cooldown.
Expand Down
8 changes: 6 additions & 2 deletions loopx/control_plane/heartbeat/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
"Local outcomes never replace root continuation, budget or stop criteria."
)

HEARTBEAT_QUIET_COMPLETION_RULE = (
"DONT_NOTIFY: no chat text. Return any required host quiet result in full."
)
USER_TODO_FINAL_MESSAGE_RULE = (
f"{OPERATOR_LANGUAGE_RULE} "
"`interaction_contract.user_channel.notify` controls output: "
"NOTIFY=concrete action; DONT_NOTIFY=quiet. "
"NOTIFY=concrete action. "
f"{HEARTBEAT_QUIET_COMPLETION_RULE} "
"`should_run`/due monitor/other-agent todos are not user prompts. "
"Only under NOTIFY, `action_required` without an action: say the specific "
"user Todo is not projected and repair LoopX state projection. Under "
Expand All @@ -31,7 +35,7 @@
HEARTBEAT_NOTIFICATION_RULE_SHORT = (
f"{OPERATOR_LANGUAGE_RULE_THIN} "
"`user_channel.notify` controls OUTPUT only: NOTIFY=show; "
"DONT_NOTIFY=no output. "
f"{HEARTBEAT_QUIET_COMPLETION_RULE} "
"`heartbeat_recommendation.agent_must_attempt`/"
"`execution_obligation.must_attempt_work`: true=work+writeback; only "
"false permits no-op. Due/peer work is not a user prompt. Missing NOTIFY "
Expand Down
16 changes: 9 additions & 7 deletions loopx/control_plane/heartbeat/task_body.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
DEFAULT_MATERIAL_QUEUE_RULE,
DEFAULT_PERMISSION_RULE,
HEARTBEAT_NOTIFICATION_RULE_SHORT,
HEARTBEAT_QUIET_COMPLETION_RULE,
HEARTBEAT_TURN_BOOTSTRAP_RULE,
HEARTBEAT_KNOWN_CHOICE_RULE,
HEARTBEAT_VISION_WRITEBACK_RULE_SHORT,
Expand Down Expand Up @@ -161,8 +162,7 @@ def render_heartbeat_task_body(
Still do not launch/stop/restart/sync/design code or mutate production unless
`should_run=true` or the user explicitly authorizes it.
- Otherwise, do not do implementation work, adapter work, file edits, research,
or project exploration in this turn. Return a quiet heartbeat `DONT_NOTIFY`
response with the skip reason.
or project exploration in this turn. Follow the user-channel rule above.

If the result says `should_run=true`:

Expand Down Expand Up @@ -367,12 +367,13 @@ def render_compact_heartbeat_task_body(
`agent_read_required`: drain/read/triage before work; settle/ACK.

Output policy: authority=`interaction_contract.user_channel.notify`;
external=`NOTIFY`; quiet=`DONT_NOTIFY`; quiet_missing_action=`internal_repair`.
external=`NOTIFY`; quiet_missing_action=`internal_repair`.
{HEARTBEAT_QUIET_COMPLETION_RULE}

If `should_run=false`: `monitor_quiet_skip` -> receipt/stall; quiet unless
replan; failed write -> retry id; no edits/spend; receipts do not self-stop.
Only under `NOTIFY`, `state=operator_gate`/`notify_user_on_open_todo=true`
permit concrete blocker-push; else quiet.
permit concrete blocker-push.
Honor repeat/cooldown. `safe_bypass_allowed=true`: validate/settle its allowed work.
Wait: one allowed read-only poll; unchanged quiet, new evidence follows the
current monitor/auxiliary contract.
Expand Down Expand Up @@ -680,9 +681,10 @@ def render_thin_heartbeat_task_body(

{HOST_LOOP_SAFETY_RULE}
{scope_sentence}
Use TurnEnvelope: `action.must_attempt` requires work; `user.notify` controls
output only. ok=false -> recovery, no delivery. Language=user. Honor `boundary`, `execution_policy`, `contract_capsule`,
`work_context`, remaining `required_reads`, `agent_context`, and `replan_action_packet` when present.
Use TurnEnvelope: `action.must_attempt`=work; `user.notify`=output only.
{HEARTBEAT_QUIET_COMPLETION_RULE}
ok=false: recover, no delivery. Language=user. Honor `boundary`, `execution_policy`, `contract_capsule`,
`work_context`, remaining `required_reads` and `replan_action_packet` when present.
When present, read inline `agent_context` before planning. Read `detail_ref.full_decision` for
selection, replan, missing/truncated commands or an `agent_context.detail_ref`:
same-invocation observation, not fresh admission.
Expand Down
2 changes: 2 additions & 0 deletions tests/control_plane/test_heartbeat_agent_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def test_thin_cli_preserves_readable_admission_and_authority_with_host_scope(tmp
body = packet["task_body"]
assert "Equal peer `worker-a` (peer_v1)" in body
assert f"scope: {scope}" in body
assert "DONT_NOTIFY: no chat text" in body
assert "required host quiet result in full" in body
assert "quota claim/lease and workspace contract plus repository rules" in body
assert "Follow todo continuation policy" in body
assert "Task-scoped coordination grants no authority over other agents" in body
Expand Down
17 changes: 17 additions & 0 deletions tests/control_plane/test_heartbeat_notification_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ def test_rendered_task_bodies_keep_execution_obligation_authority() -> None:
assert "Missing vision: same-turn checkpoint-context" not in body


def test_codex_app_quiet_completion_is_host_control_not_chat(tmp_path) -> None:
variants = [{mode: True} for mode in ("full", "compact", "brief", "thin")]
variants.append({
"thin": True, "turn_envelope": True,
"decision_output_root": tmp_path, "turn_instance_id": "fixture-quiet-turn",
})
for variant in variants:
body = build_heartbeat_prompt(
goal_id="quiet-completion-fixture", agent_id="worker-a",
registered_agents=["worker-a"], runtime_profile="codex_app_heartbeat",
**variant,
)["task_body"]
assert "DONT_NOTIFY: no chat text" in body, variant
assert "required host quiet result in full" in body, variant
assert "response with the skip reason" not in body, variant


def test_all_heartbeat_modes_scope_no_spend_to_observation() -> None:
# Build the shipped payload, including the short/default worker entrypoint.
# An unchanged artifact may still accompany a validated route elimination;
Expand Down
Loading