Skip to content

Heartbeat Automation Prompt

This is the public copy-paste template for a Codex App heartbeat automation that advances one LoopX goal without hiding compute policy inside the timer.

The timer only wakes the executor. LoopX decides whether that wakeup should spend delivery compute.

The unified-contract RFC keeps Codex App heartbeat as a required product qualification path. Its proposed controller session would consume one canonical contract and delegate ordinary work to an executor. That profile is not enabled by this template: continue to use the installed runtime's fresh contract and existing scheduler/stop rules.

中文:统一契约提案 将 Codex App heartbeat 列为必验产品路径。拟议 controller 消费统一契约后向 executor 交付业务任务;本模板不启用该模式,仍以已安装运行时的最新契约和调度/停止规则执行。

Two Prompt Layers

Keep the Codex App visible goal text and the heartbeat automation task body as two separate layers:

  • Visible goal text: short and human-scannable, for example 按 ACTIVE_GOAL_STATE.md,基于 LoopX 体系,推进项目.
  • Persistent heartbeat entrypoint: generated by loopx heartbeat-prompt --bootstrap --thin --codex-app; save the v2 loader in the automation. It binds the task's goal and agent and fetches the current contract on every wake.
  • Execution or audit body: generated without --bootstrap. Thin is the normal fresh execution body; compact, brief and full expose other levels of detail. These changing bodies are not the persistent automation prompt.

Prompt compression must preserve both conditions and required responses. A missing concrete user action under NOTIFY calls for state-projection repair, not just a diagnostic label. Under DONT_NOTIFY, repair stays internal; it does not grant notification authority. Keep these semantics in brief and thin prompts as well as the expanded contract, within their existing size budgets.

Reward Memory outcome guidance is a deliberate prompt increment, not a reason to remove the validator, digest/evidence, writeback/readback, zero-provider-call, or private-material boundaries. When automatic outcome ingestion is enabled, the generated heartbeat body receives a fixed +640-character headroom (and the corresponding bounded line/UTF-8 allowance) for this contract. The CLI output differential also grants that allowance once during the none-to-v1 migration; unrelated later growth still uses the ordinary gate. Keep the explanation readable, while visible Goal prompts and other surfaces retain their own limits.

All generic heartbeat and native Goal bodies match the user's current language, fall back to English when no user language is available, and do not mix languages unless the user asks or a scoped capability requires bilingual output. This policy applies to full, compact, brief, thin, visible Codex and TraeX Goal, and Ark Managed Agent bodies. Capability-specific bilingual contracts remain authoritative for their own output surfaces.

Do not paste the full lifecycle protocol into the visible goal text, and do not use a short goal text such as "advance TODO" as the recurring automation body. The short text names the goal; the generated task body enforces quota, gates, steering audit, writeback, refresh, and spend accounting.

Ark Managed Agent is not an automation profile. Its integration uses one transport-neutral goal prompt and lets the goal runtime own inner iteration; see the host integration protocol instead of adapting this recurring automation contract.

Native Goal bootstrap and live execution instructions

Full, compact and brief automation use the same fully qualified notification/execution rule as thin, including heartbeat_recommendation.agent_must_attempt and execution_obligation.must_attempt_work. None embeds a second static refresh/spend sequence: after validated work it follows the current interaction_contract.cli_channel.settlement_plan.ordered_steps, or current next_cli_actions when there is no plan. Generator command fields remain for compatibility, not as a stale fallback. Todo acceptance alone is not Turn settlement or terminal vision closure. The brief base budget is 4,800 characters; reward-memory headroom remains separate.

Markdown prints a complete guard command once when the successful task body already contains that exact command as a whole line. Generator Inputs then points to the body; JSON fields remain complete. Missing, different, partial or failed bodies retain the complete diagnostic command.

成功正文已按完整行包含同一 guard 命令时,Markdown 诊断区指向正文,避免重复长命令; JSON 字段保留全文。正文缺失、不同、仅匹配前缀或生成失败时,诊断区仍打印完整命令。

Brief 与 thin 共用完整执行义务路径;full、compact、brief 均移除固定结算配方,而不是 删除结算义务。真实 App preflight、registry scope、完整 guard 和静态安全规则 均保留;结算顺序与身份以本轮动态 contract 为准,vision replan 不由历史成功清账。

First-connect messages and supported host activations use heartbeat-prompt --bootstrap: a saved loader requests the installed rules rather than freezing a long execution body. The loader and automation bootstrap share rendering and successful-response checks. Registry-derived state is resolved at load time; explicitly supplied policy remains bound. The inner command does not request another bootstrap. Claude Code loads its inner body through the bound MCP host_prompt tool. TraeX's separate capability projection remains separate, not embedded by this loader. See prompt upgrade lifecycle for automatic exact-managed adoption during update --apply, including the closed-App SQLite/TOML adapter and conflict recovery boundary. Running Apps require their native automation API.

Static semantics retained across hosts

Thinning removes duplicated recipes, not authority boundaries. The shared runtime body keeps repository rules, credential/private-material protection, explicit authorization for destructive Git/production actions, and exception routes (loopx-project for lifecycle/registry, loopx-self-repair for drift). These routes are conditional, not mandatory skill calls on every iteration. Ordinary Claude MCP iterations still use MCP; the CLI route is not a second accounting path.

Semantic Owner after thinning
Privacy, repository rules, dangerous-action authority Shared static safety rule; a trusted host is not blanket permission
Lifecycle or projection repair Conditional static repair route; repair does not bypass gates
Selection, claims, vision replan, exact settlement identities/order Current successful interaction contract, not saved command recipes
Blocked path vs whole Goal Gate only the affected path; continue independent admitted work; only terminal no-follow-up completes the Goal
Git branch/worktree/PR policy User and repository rules; no generic No project branches restriction
Prompt authoring/maintenance advice Documentation, not per-iteration executor instructions

For heartbeat shells, assign LOOPX_TURN in a separate statement before the guard, in the same shell invocation. A command-prefix assignment does not make the variable available to argument expansion in Bash/zsh. Reuse the same value on retries. Native Goal entry remains host-specific and does not inherit this heartbeat bootstrap.

Thin's presentation regression ceiling is 3,600 characters; compact remains 6,700. The same 320-character scope plus observed-capability fixture grew from 3,172 to 3,577 normalized characters: explicit reuse of existing action authority, continued integration/negative-case/readback work, recovery evidence and exact current settlement are retained rather than abbreviated away. This is a measured presentation allowance, not an external host limit, execution quota or permission increase. The supported registry-profile brief fixture grows from 4,447 to 4,638; its base ceiling is 4,800 rather than 4,600. Reward-memory headroom remains separately feature-scoped. Larger custom inputs can still exceed a presentation budget and must be measured as supplied. Peer workspace and claim/lease requirements come from the current quota contract and repository rules; registration alone does not impose an independent worktree. Repository-specific worktree requirements still apply.

The automation lifecycle is the reference for shared execution, not a wrapper around native Goal behavior. Thin automation and Codex CLI/SSH, TraeX and Ark Managed Agent Goal bodies share quota dispatch: selection/re-entry, admitted work and validation, then the current writeback/settlement instructions. They do not share scheduler ownership, host completion, or blocked/resume rules.

Native Goal bodies share a compact bootstrap. Generate it with the host's existing profile (for example heartbeat-prompt --runtime-profile codex_cli --goal-id <GOAL_ID> --agent-id <AGENT_ID>). The persistent body binds the Goal/Agent and quota entrypoint; each work iteration reads the current complete, successful quota JSON. The inner execution instructions remain in interaction_contract, including selection/re-entry, admitted work, and exact cli_channel.settlement_plan.ordered_steps.

Native Goal bodies no longer embed a second static accountable refresh/spend template. Those command fields remain available in the generator response for compatibility/inspection, but are not a fallback for the live settlement plan. When no ordered settlement plan applies, consume the current next_cli_actions, including any required re-entry; do not substitute a saved generator command. Execute selection/re-entry before work and writeback/spend only after the corresponding validated work; a projected accounting command is not evidence that work happened. Preserve the plan's identity and flags, and follow readback or recovery after an ambiguous write instead of retrying a guessed command. Failed or incomplete contract reads permit neither work nor spend.

An unbound Codex CLI or Ark Goal with selected Todo/replan work now receives a quota re-entry template with --turn-instance-id. Fill it with one public-safe unique work-iteration id and reuse that id on retries. The next packet supplies the same ordered settlement machinery used by automation, with visible-goal attribution. SSH Goal continues to use its existing --begin-turn path. This fixes the previous unbound native refresh/spend projection: those commands could not satisfy the existing settlement identity guard. It does not turn CLI/Ark Goals into App heartbeat receipts or move scheduler ownership into LoopX.

The bootstrap retains work-sizing guidance and the distinction between progress and Goal completion. A new Todo is not a new host Goal; quiet/blocked states are not terminal no-follow-up. Codex alone retains its native blocked/resume rule. User/repository authority still applies; a trusted host is not blanket permission. This changes newly generated native Goal bodies and thin automation dispatch, not active host Goal objectives or benchmark prompts already pinned to a run. An installed runtime supplies updated dynamic contracts on later reads; upgrading it does not retroactively remove old text from an existing Goal.

Current instructions follow one chain: fresh guard → typed work_context and pending required_reads → full quota or TurnEnvelope → Host. Use these sources for a bounded working brief of evidenced progress/change, current blockers, next work, acceptance and stops. Missing comparison evidence is unknown, not unchanged. Preserve complete requirement reads and original settlement identity; do not freeze a progress summary into the recurring automation or call another model merely to rewrite it. A changed source requires the existing re-entry. This is context delivery and Agent guidance, not proof of adoption or improvement.

Behavior change: generated full/compact/thin bodies and the managed project skill no longer invent two-stall replan/self-pause thresholds or a fixed count of alternative candidates. All modes defer to current Core obligations and explicit stop boundaries. Existing Core stall detection and periodic-review cadence remain; local correction preserves valid work, a due review may retain its path, and broader changes need relevant current evidence. The generic external worker also rejects non-boolean admission before its configured wake command; a terminal directive still wins. App-owned automations require the existing Host stop/update and readback path, so this does not promise a pre-model App dispatch hook.

Claude Code's MCP-backed loop.md follows the same work-sizing rule and current quota contract, without a fixed one-segment limit or empty-Todo-list completion shortcut. Its complete_task tool already owns the ordered writeback/spend transaction: callers must not perform a second accounting sequence through CLI. Partial work is not Todo completion. Native /loop remains Claude's scheduler; only the current Goal's wakeup may be cancelled after terminal no-follow-up. These changes apply when loop.md is regenerated, not by editing active user files. The MCP tool now exposes successor_todo_ids, reusing CLI/TS completion semantics to link known follow-up without creating another Todo. Ordinary acceptance and Turn settlement are distinct: the adapter validates/completes work before its writeback/spend; only terminal closeout requires the full receipt chain. This removes the former delivery-class circular prerequisite, not validation or accounting. See the release test guide.

For Codex App, the generated quota command carries the compact explicit runtime profile --runtime-profile codex_app_heartbeat (generated commands use the equivalent compact alias --codex-app). The prompt does not restate the three scheduler ownership fields as prose. Other hosts generate their real typed execution context instead of inheriting App cadence by omission. Do not hand-edit per-project lifecycle branches into one automation prompt. Project-specific behavior belongs in the LoopX registry, active-state sections, adapter output, or narrow boundary rules. If a lifecycle rule is generally useful, add it to loopx heartbeat-prompt and its smoke contract so every project inherits it. Executable versus watch-only agent follow-up should be registered through the todo CLI (--task-class and optional --action-kind), then projected by quota should-run; it should not be encoded as benchmark- or project-specific prompt text. When the generated prompt and the installed skill disagree, the worker should trust the current CLI interaction_contract first, then use the skill as the operation manual and the prompt only as a bootstrap. Within that contract, the worker should execute agent_channel.primary_action. An optional agent_channel.resolution_trace is for debugging route selection and drift; it should not be treated as another action to run or as permission to rewrite active-state Next Action.

For a recurring Codex App automation, generate the persistent entrypoint:

loopx --format json --registry <GLOBAL_REGISTRY> heartbeat-prompt \
  --bootstrap --thin --codex-app --goal-id <GOAL_ID> --agent-id <AGENT_ID>

Require ok=true and save the returned task_body through automation_update. It starts with LoopX managed heartbeat bootstrap v2 and loads the current thin contract on every wake. The embedded command omits --bootstrap, preventing recursive loaders. New $loopx App activation and existing automation upgrades use this same renderer. Native /goal hosts retain their own scheduler binding.

--thin, --compact, --brief and --full without --bootstrap return the current execution or audit body; do not save that changing policy as the recurring prompt. Connected goals resolve their active-state path from the registry. Explicit detached-state overrides remain available through --active-state.

Use loopx automation-prompts plan --codex-home <ACTIVE_CODEX_HOME> to inspect existing prompts, then apply the reviewed desired_prompt through the App API. Preserve each automation's own goal/agent/task binding, cadence and notification setting, and read back the result. The offline adapter requires the App closed; while running, its cached scheduler can overwrite direct database/file changes. Installing a binary or changing a TOML file alone does not prove migration.

When multiple agents share the same project control plane, first register the public-safe agent ids on the goal, then give each automation an explicit identity and natural-language scope:

loopx configure-goal \
  --goal-id <GOAL_ID> \
  --registered-agent codex-main-control \
  --registered-agent codex-side-bypass \
  --agent-model peer_v1 \
  --execute
loopx heartbeat-prompt \
  --goal-id <GOAL_ID> \
  --bootstrap --thin --codex-app \
  --agent-id codex-main-control \
  --agent-scope "benchmark readiness, benchmark execution, and benchmark writeback"

For another peer, use a different id and a disjoint scope:

loopx heartbeat-prompt \
  --goal-id <GOAL_ID> \
  --compact \
  --agent-id codex-side-bypass \
  --agent-scope "control-plane coordination and todo claim ergonomics" \
  --agent-scope "do not take benchmark execution todos unless reassigned"

The generated body tells the agent to claim only in-scope todos with loopx todo claim --claimed-by <agent-id>. --agent-scope requires --agent-id, and the CLI accepts that agent id only when it is registered for the goal. Scope stays in the automation prompt or handoff; todo metadata records only the soft claimed_by owner. Registered identities are peers. Functional profile roles are advisory, while workspace isolation and continuation behavior come from the selected task, goal policy, and typed continuation policy. Generated scoped heartbeat commands pass the same --agent-id to both quota should-run and quota spend-slot, so workspace guards and quota accounting evaluate the same identity.

Host capabilities are declarations, not permission grants. When the selected Codex App, CLI, or external launcher already has a capability required by its todos, declare it while generating the heartbeat:

loopx heartbeat-prompt \
  --goal-id <GOAL_ID> \
  --thin \
  --agent-id <AGENT_ID> \
  --available-capability network \
  --available-capability external_evidence_poll

The generated quota guard and spend command preserve the same declarations. Do not declare credentials, production access, or another capability merely to bypass a gate; the launcher must actually provide it.

When a thin prompt is generated without explicit declarations, it still tells the runtime worker to project non-basic capabilities that are actually present with --available-capability. This prevents an observed network or polling capability from becoming a false user gate without guessing capabilities the host does not have. Explicit generator arguments remain preferred for hosts whose capabilities are known when the automation is installed.

  • for small AGENTS-eligible validated changes, self-merge and complete the todo with --self-merged --evidence "<commit and validation summary>";
  • for an independent continuation, create --next-agent-todo and optionally select a registered peer with --next-claimed-by;
  • when independent review is required, use --next-action-kind review with an ordinary independent_handoff; add --next-excluded-agent <author> only when the author must not reclaim the unclaimed successor;
  • when a validated pull request merely needs human review, keep the reminder non-blocking with --next-user-todo "<review action>" and --next-user-task-class user_action, then create the next runnable agent todo in the same completion. Add a separate continuous_monitor for the PR lifecycle when merge/readback must be observed. Do not turn review latency into a gate;
  • when an open advancement Todo reaches an external-only wait before completion, do not end the heartbeat after polling it. Keep it open and atomically bind resume_when=monitor_changed:<monitor-todo-id> plus an independent runnable --successor-todo-id, rerun quota, and continue the successor. The typed wait transition is a no-spend lifecycle repair; only later validated delivery is accountable work;
  • every selected Todo-bound, delivery-enabled must-attempt guard persists closeout_required=true. A fresh heartbeat checks the immediately preceding flagged guard against its exact writeback/spend receipts and typed Todo lifecycle. If neither is present, unsettled_host_turn_recovery_v0 preempts ordinary work selection. For an open advancement Todo without a resume gate, the typed resume_prior_turn route re-enters the original Turn guard: inspect existing effects first, follow its current eligibility and settlement contract, and account only verified work under that original identity. Missing receipts are not evidence of an external wait; never invent monitor_changed or a successor to clear recovery. After legal closeout, rerun the same current Turn and continue eligible work. Recovery selection itself is idempotent and no-spend; actual validated delivery retains normal accounting. Genuine external waits and monitor observations retain their existing typed closeouts. Receipts created before the explicit flag are not retroactively treated as unsettled. Exact-ID lifecycle reads include retained Completed Work Archive rows, while ordinary Todo lists remain active-only; an archived terminal fact closes recovery without rewriting archive history;
  • use user_gate only for an exact authority boundary such as approval to merge an aggregate branch into main, release, launch a benchmark, or perform a protected action;
  • when work is blocked without a valid successor, keep the todo with the current peer and write a concrete blocker rather than inventing a hierarchy route.

Once a goal has coordination.registered_agents, prompt generation without --agent-id fails closed. That is the lightweight migration signal for stale Codex App automations: the next refresh attempt surfaces a concrete identity/scope upgrade command instead of returning a legacy unscoped prompt. quota should-run follows the same rule for executor safety: an unscoped call returns automation_prompt_upgrade.required=true, blocks_should_run=true, and should_run=false instead of allowing delivery. For a hierarchy-era registry, quota should-run and upgrade-plan return one stable migration id, one heartbeat command per registered peer, and a completion command. The host update may be retried with that idempotency key; the completion command atomically records the migration once, and later quota checks do not project it again. A registry without coordination.registered_agents must first register the peer identity before a scoped prompt can be generated.

To inspect a smaller execution body for debugging, generate the brief body:

loopx heartbeat-prompt \
  --goal-id <GOAL_ID> \
  --brief

This is an execution/audit view, not the persistent automation prompt. Use it only when the target agent has LoopX CLI access. It is intentionally small and treats the compact/full lifecycle contract like a skill-style interface that the agent fetches when quota says real work can run or when an edge branch is ambiguous.

To inspect the thin execution body for the current turn:

loopx heartbeat-prompt \
  --goal-id <GOAL_ID> \
  --thin

Use the thin body only when the controller is expected to do a fresh registry/quota/state/status/repo inspection on each wakeup. It should stay project-agnostic; if a behavior needs to be remembered across workers, write it to active state, run history, the registry, or a generated prompt contract rather than hand-editing the automation body.

loopx heartbeat-prompt --thin --format json emits the versioned heartbeat_agent_input_v1 Agent-input envelope. Its compact interface_budget reports mode, normalized budget_char_count, max_chars, and within_budget; generator diagnostics and duplicate commands stay out of the recurring Agent hot path. Human-readable Markdown and non-thin JSON modes retain the richer generator packet, including char_count and line_count. upgrade-plan --format json also carries that richer budget summary inside each generated prompt, so local default-promotion checks can flag prompt bloat without parsing prose or relying on a chat thread.

upgrade-plan --format json also carries a compact prompt_policy_audit for installed prompts when their body is available through the local Codex App automation record or an explicit manifest. The audit does not echo the prompt body. It only reports warning kinds such as a generic should_run=false hard-stop appearing before safe-bypass handling, embedded project policy blocks, or pinned --active-state arguments. Any warning should be treated as upgrade work: regenerate the installed heartbeat from the current CLI contract and keep project-specific policy in registry/state/status/review-packet payloads.

For gray rollout, generate the brief body through loopx-canary and pass --cli-bin loopx-canary so only the selected goal controller uses the live checkout:

loopx-canary heartbeat-prompt \
  --brief \
  --cli-bin loopx-canary \
  --goal-id <GOAL_ID>

Opt-in native context delivery

--context-delivery {off,on} defaults to on for supported native Codex App/SSH and benchmark heartbeat entries; save the machine setting as off or pass --context-delivery off to disable. An explicitly saved off is preserved. Other hosts and ordinary CLI keep full delivery. It reuses existing scheduler-owned host declarations and the benchmark worker's explicit execution settings; it does not guess a host from process names or ambient native session variables. These declared run modes can activate it:

Supported entry Existing host declaration
Codex App heartbeat, same-session resume and recovery --codex-app, equivalently --runtime-profile codex_app_heartbeat (or its complete explicit host tuple)
Interactive Codex App SSH Goal --runtime-profile codex_app_ssh_goal; retains native --begin-turn and visible-Goal settlement
Codex benchmark heartbeat worker Existing generic_cli profile plus the worker-owned LOOPX_EXECUTION_MODE=heartbeat and LOOPX_CONTEXT_DELIVERY=on declarations; its execution owner rejects other modes

--codex-app is a shortcut for a complete runtime profile. --host-surface alone is only one field. With on, missing, incomplete, contradictory or unsupported run-mode declarations are configuration errors: the CLI exits nonzero with a reason before admission or transport writes, including in native hooks. Correct the declaration or select off; there is no owner question or new approval. In a supported run mode, a missing or invalid continuity basis still delivers the same original full capture. The current recovery reference is saved before optional delta computation, so an oversized warm request or failed candidate update cannot select an older capture after compaction or resume. If the current recovery cannot be saved, the old session lookup is invalidated; use the explicit original capture reference. If preparation fails before that can be established, or invalidation also fails, the CLI reports quota_decision_transport_failed with the saved decision and receipt. Do not rerun admission to recover a presentation failure.

When the caller also selects --record-host-poll, the existing liveness owner records that observation once before the original capture. The saved decision, ordinary/envelope output and native recovery view retain the same host_poll_receipt, including an unreadable-receipt diagnostic. A capture or handoff failure still reports an already-recorded poll; its count can advance even when presentation fails. Reading the original evidence never records another poll, launches work or spends quota. Without this flag, capture and context delivery do not create a liveness receipt.

off creates no native journal, hook configuration or input observation; directories alone never turn it on. This declaration check is not host authentication, execution authority or proof of retained input. Native delivery uses the explicit lifecycle assumption described below; final-input Host clients keep their independent retained-input confirmation contract. Other generic CLI and native Goal profiles, Claude and other hosts remain outside this opt-in. Harbor/container and installed desktop adoption retain their separate unqualified boundaries; listing an entry does not certify live adoption.

For a trusted Codex host, render the native setup through the installed CLI. Private context/capture directories are selected and created automatically under the resolved machine runtime; advanced callers may override those locations:

loopx --format json quota context-config --context-delivery on --codex-app \
  --goal-id <goal> --agent-id <agent>

The result contains native_config.config_overrides, native_config.hooks and native_config.developer_instructions. Merge the hooks into the host's existing hooks.json or inline hook configuration, and append the recovery instruction to its existing developer instructions. Apply each flat key/value in config_overrides to the native host configuration (or as a -c key=value override). This selects 64000-byte context continuations, a 20000-token history limit, and a shell-scoped reader budget declaration. The initial guard uses the same output budget. Its instruction requests 22000 tokens per reader result and 24000 for code-mode emission, with one directly forwarded stdout per call. Native paging uses this default without the shell declaration; the isolated benchmark worker applies this profile automatically when opted in. Keep short context readers in the foreground (exec_command's normal 10000 ms wait). Early output followed by polling can leave the native post-tool hook with only an empty tail, so a visibly complete page still cannot establish a baseline. Consume every chunk if a command yields and preserve full fallback when hook coverage is incomplete; do not re-admit or invent a retention receipt. Preserve other configuration. Review and trust only these exact hooks through Codex's native hook trust surface; the CLI does not install files, approve hashes, enable diagnostics or run a guard. Rollback removes these generated output overrides along with the context hooks and recovery instruction; restore any previous host values. The profile does not establish model retention, task gains or net fee savings. An already loaded native thread may keep its earlier hook configuration even when hooks/list discovers the new handlers as enabled and trusted. That readback does not deliver SessionStart or initialize its context journal. After active work reaches its normal safe boundary, use the host's start/resume path with the resulting configuration and verify actual lifecycle delivery before qualifying live use. Do not restart an active host merely to manufacture a sample. Until then, preserve full delivery and the original capture; do not fabricate events or borrow another session's journal. Use one configuration for the declared Goal/Agent. A shared private root keeps different native sessions in separate journals, including interleaved sessions. For an interactive SSH Goal, use --runtime-profile codex_app_ssh_goal in place of --codex-app for both setup and the thin prompt. It retains the 4000-character native Goal limit and the full command paths, with the same live contract and native blocked/resume rules; it does not become a hosted heartbeat.

Generate the normal bootstrap with heartbeat-prompt --thin --codex-app --bootstrap, its existing Goal/Agent/registry arguments, and --context-delivery on; optionally add --turn-envelope or explicit private paths. The loader preserves these options without freezing a Turn ID. Each wake loads current instructions and binds the existing LOOPX_TURN before its generated guard. Selection keeps the same transport. An unset wake identity fails in the shell before admission.

Native guards and JSON recovery readers use the existing 64000-byte output profile even when an App shell lacks the configuration environment override. Small captures finish inline; larger captures continue with the same large-page budget. Explicit smaller budgets remain available for truncated-range recovery. The native hook accepts complete JSON records alongside other shell output and still verifies each record against the trusted original capture before reuse.

Native reuse assumes a normally completed full read was read and remains available until a lifecycle invalidation. PostToolUse merges complete reader ranges in the existing private session journal. EOF alone, known read failures, malformed/truncated pages and interrupted paging cannot establish a baseline. Compaction, clear, fork, session/workspace change and unqualified SessionStart invalidate it; ordinary tools and turn endings do not. A same-session resume can retain its baseline only when the native replay checkpoint verifies continuity. Read the pinned original completely before establishing a new differential chain. Repeated events and parallel pages are idempotent. Workspace identity comes from native lifecycle events, not a shell command's working directory. The same native session can run projection and recovery commands in a subdirectory or another checkout without discarding its baseline. A native SessionStart reporting a changed workspace still retires the previous candidate and recovery selector. Session, Goal and Agent isolation remains in force; changing command directories grants no work or publication authority. 工作区身份由原生生命周期事件确定。命令切换到子目录或另一 checkout 不会清除同一 原生会话的差分基线;原生 SessionStart 报告工作区变化时仍清除旧基线和恢复选择器。 Session、Goal 和 Agent 隔离保持不变,切换命令目录不授予工作或发布权限。 This replaces per-request input-history proof, not the capture integrity check. It preserves native providers, authentication, endpoints and connection mode, including implicit history. No input diagnostics or forced HTTP is required. Unreported downstream output loss or missed/externally disabled hooks can escape detection; this is an engineering assumption, not measured retention or a model ACK.

Read back native hook discovery/trust and the generated guard options, then verify cold full, warm delta after completed reader delivery, resume and compaction recovery on the actual host. The persistent instruction names quota context-read --codex-context-root ... when an entire hook is lost; lookup is read-only and subsequent pages pin the original file/hash. It never readmits or spends. Normal native trust/settings UI remains the activation and error surface; failed setup keeps the full path. Explicit off stops projection. Replacing machine configuration also invalidates reuse, so an off/on round trip cannot reuse a baseline that missed lifecycle events while disabled. An existing decision-handoff socket may use this same capture. Handoff validates and releases the immutable original first; native projection follows release. It does not create a second checkpoint or change the handoff's failure/recovery rules. If interrupted while held, consume the original full continuation packet.

For a host that already saved the quota decision before invoking Codex, present that immutable snapshot from the native tool instead of running its guard again:

loopx --format json quota context-project --context-delivery on --codex-app \
  --capture-file <original-quota-json> --capture-sha256 <original-sha256> \
  --goal-id <goal> --agent-id <agent> --turn-instance-id <original-turn> \
  --codex-context-root <private-context-root> --decision-output-root <private-capture-root>

The JSON response supplies mode, the digest-pinned read command, the target when available, and an original full recovery reference. Cold mode delivers complete context plus its target; a warm delta names its native-hook continuity basis. Follow every page and reconstruct before dependent work. Optional codec or delivery-cache failure after recovery publication falls back to that same full capture. Enabled native preparation or unsafe recovery publication fails with ok: false, quota_decision_transport_failed, and the original recovery reference and receipt; follow that explicit reference instead of a session lookup. Invalid source hash or Goal/Agent/Turn refuses publication. This is integrity-checked presentation of the caller's snapshot, not fresh authority, registry lookup, installation, admission or accounting. The original file stays unchanged; generated caches remain private. A missing projection command can use quota context-read with the original file/hash. Set --context-delivery off and remove context options from generated host configuration to restore ordinary full-read instructions. Remove only these generated hook entries and appended recovery instruction from native configuration. Preserve unrelated diagnostics, providers, hooks and settings; the CLI does not edit host configuration. No stored decision or receipt is rewritten. To roll back, remove the context/capture options from this bootstrap and remove only the generated hooks/recovery instruction from the native configuration. Existing captures remain private recovery evidence. This opt-in does not grant execution authority or prove installed App adoption or net token savings.

Template

Generate the persistent prompt from the installed runtime:

loopx --format json heartbeat-prompt --bootstrap --thin --codex-app \
  --goal-id <GOAL_ID> --agent-id <AGENT_ID>

Read the successful result and install its task_body through the authorized host automation API, preserving the existing automation identity, schedule and notification preference. Read back the saved prompt and status before claiming activation. Omit --agent-id only for a goal with no registered peer requirement. For native Goal hosts, use their actual --runtime-profile instead of --codex-app.

Optional --active-state, material and permission overrides bind only explicitly selected policy. Connected goals normally resolve state from the current registry. Do not hand-copy a generated execution body, Turn id, settlement command or project policy into this persistent template. The loader fetches those current instructions on every entry; an unsuccessful/incomplete read authorizes neither work nor spend.

Minimal User-Facing Form

Keep the visible objective short, for example “Advance this Goal within its current authority and acceptance criteria.” Set the generated host-specific loader as the execution entrypoint. The visible objective is not an alternative quota, authorization or settlement contract.

The old hand-written full and minimal templates duplicated runtime rules and could direct a stale refresh/spend or reuse the wrong host binding. They are replaced by the generator above. Existing saved prompts need the ordinary upgrade/readback flow; editing this document does not migrate active automations or pinned benchmark runs.

Host Scheduling And Stop Readback

When creating a heartbeat in Codex App, keep the visible instruction short and use the generated bootstrap loader as the automation task body. The default onboarding cadence starts at 3 minutes; after the first guard, follow quota should-run.scheduler_hint to back off long waits and stop external loops after a final quota/replan check confirms repeated unchanged polls. App-hosted heartbeats should search/use automation_update when available. If scheduler_hint.action=stop_until_explicit_resume and scheduler_hint.app_automation.host_action=pause_or_delete_current_heartbeat: in that stop case, call automation_update once to pause the current heartbeat, verify the host result, spend no quota, and end the turn without a scheduler ACK. Full, compact, brief and thin generated bodies retain this same stop clause. If the tool is unavailable or the result does not confirm the current heartbeat stopped, leave that outcome unresolved; do not claim success, retry, change the RRULE as fallback, or perform new work/spend. Output still follows the user notification contract. An App-owned wake may already have invoked the model before it reaches this guard. Regenerating a body does not update an existing installed automation: apply the new body through the existing host update flow, retain its identity, and read back its prompt/status. Otherwise call it only when scheduler_hint.app_automation.stateful_backoff.apply_needed=true and scheduler_hint.app_automation.recommended_rrule is present. After a successful RRULE update, run loopx with scheduler_hint.app_automation.ack_hint.cli_args; current payloads use quota scheduler-ack-current so LoopX re-reads the latest hint and owns the progression/reset state. The ACK settles that RRULE; an immediate final guard may verify the same target but must not be treated as another elapsed poll. Attempt the host update at most once per hint and turn. If it fails or times out, do not retry or ACK; run scheduler_hint.app_automation.failure_hint.cli_args once to persist the failed target and observed host RRULE without spending quota. Exact repeats are then suppressed until either value changes. Continue any allowed delivery under the observed host cadence. When the desired RRULE is already applied, skip automation_update; if stateful_backoff.ack_needed=true, run the bound ack hint directly, otherwise do nothing. For the uniquely matched current heartbeat, quota should-run reconciles the installed RRULE with the ACK ledger; a host_observation.status=drift_detected result reopens apply_needed.

If automation_update is unavailable in the session and scheduler_hint.app_automation.fallback_hint.available=true, run the bound fallback_hint.cli_args (loopx-apply-rrule) once instead. The fallback requires the projected registered --agent-id; there is no implicit Agent default. It backs up codex-dev.db, syncs the automation TOML and SQLite row, and runs the bound ACK; direct SQLite edits bypass the app API, so this is a bounded fallback and never the routine path. The bridge reuses the provided parent Turn for its internal quota should-run query. That same-Turn replay preserves the committed receipt's bound Todo, observed capabilities, and settlement identity; an explicitly conflicting identity still fails closed. When the automation id could not be resolved, fallback_hint.available=false and the pasteable heartbeat gate is the correct stop - never guess an automation id.

Agent Checklist

For every automatic heartbeat turn, the agent-facing checklist is:

  1. Guard first: quota should-run --turn-instance-id <HEARTBEAT_TURN_ID> using this trigger's <current_time_iso> and reusing it for same-heartbeat retries. If the caller has already chosen the unfinished work for this new Turn, append --todo-id <chosen-id> to that first guard. This requests current qualification and complete required context in one call; it does not adopt the previous Turn's authority, read coverage or settlement. Read the current contract before work and follow rejection/recovery when the choice is stale or gated. When the choice is unknown, discover candidates and then use the returned selection_command. Never guess a Todo or change an existing Turn binding to avoid a read. Same-Turn compaction alone does not start this flow. 若本次新 Turn 已明确继续哪项未完成工作,可在首次 guard 中直接附加 --todo-id <chosen-id>,一次取得当前资格及必要上下文。旧任务引用不继承旧 权限、阅读覆盖或结算身份;仍须读取当前合同,过期或受限时按返回结果恢复。 尚未选定工作则先发现候选,再用 selection_command;不猜测任务、不为少读 一次而更换既有 Turn 绑定,同一 Turn 的压缩恢复也不触发这条新准入路径。 If loopx is not initially on PATH, export $HOME/.local/bin:$PATH and run the local installer fallback before declaring preflight failure.
  2. If should_run=false with state=operator_gate, ask the user/controller the current gate unless the same unresolved gate was already surfaced recently. 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 / 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. This notification flag does not decide delivery or accounting: interaction_contract.agent_channel owns admitted work, and interaction_contract.cli_channel owns its settlement. A pure user_todo_blocker_push lane with no admitted work does no delivery or spend; bounded_delivery_with_user_notice or an admitted safe bypass still requires its work and current settlement. If open_todo_notification_policy=repeat_until_resolved, repeat the concrete notice only while the current user channel returns NOTIFY, until the todo is done, deferred, or replaced. If user_gate_notification_cooldown.notification_suppressed=true, keep the gate pending but return quiet DONT_NOTIFY until its bounded reminder window or a material gate/host change. Otherwise, ordinary blocker-push asks may be de-duplicated when the same blocker was surfaced recently.
  4. If effective_action=outcome_floor_recovery or recovery_delivery_allowed=true, treat should_run=true as a recovery turn: run only the bounded evidence/blocker recovery and spend only after validated writeback.
  5. If the gate was already surfaced and safe_bypass_allowed=true, either take one independent safe-bypass step or report the pending gate compactly.
  6. If the current goal is eligible, dependency or sibling-goal open user todos must not stop the whole turn; record or surface them, then keep looking for a gate-independent P0/P1/P2 candidate for the current goal.
  7. Run the steering audit before choosing the work.
  8. Use attention_queue.items / project_asset as current routing authority; if project_asset is absent or legacy/raw fallback, raw queue fields are not owner/gate/stop authority. Use run_history.latest_runs only as evidence or drill-down.
  9. Follow execution_obligation before deciding quiet no-op: heartbeat_recommendation.notify is not an execution gate. If must_attempt_work=true, do one bounded progress batch or segment even when notify=DONT_NOTIFY; quiet no-op only when must_attempt_work=false and the current interaction_contract.user_channel.notify=DONT_NOTIFY. Then follow heartbeat_recommendation: first connected read-only goals should run one real read-only-map, while already mapped unchanged goals should return a quiet no-op without another dry-run or quota spend.
  10. Check delivery_batch_scale, delivery_outcome, post_handoff_outcome_gap_streak, and handoff_delivery_contract; for repeated-small or surface-only loops, obey the contract.
  11. Follow current Core repair/replan obligations. Correct the affected work first; widen only on relevant evidence and check subsequent adoption/results. A due review may keep the route. Wake counts grant no replan or self-pause authority; explicit/Core/host stops and original settlement still apply.
  12. Complete the Todo under its original declaration and current lease; do not invent a successor or --no-follow-up. Todo completion does not settle the Turn: follow its current ordered settlement, then read back continuation or terminal Goal completion. A real follow-up reuses the existing successor.
  13. Plans/top todos/route changes need LoopX todo / Next Action writeback or a no-writeback rationale.
  14. Reuse existing action-specific authority for public-safe commit, push and PR creation after validation and boundary checks. Gate missing authority only on the affected path; protect private evidence and credentials. Destructive Git, production and explicit repository review gates retain their authority.
  15. Continue authorized work through implementation, integration, negative cases and readback while feasible. Preserve evidence, remaining gaps and the next action for recovery; a tool call or writeback does not complete the Goal. Obey explicit user, Core and host stops and preserve owed settlement.
  16. Validate before reporting.
  17. After validation/writeback, follow the current typed settlement plan. An exact committed receipt-bound monitor poll already closes that Turn with no accountable refresh or quota spend, including a material poll that releases an independent successor. Other accountable delivery refreshes use explicit scale/outcome hints and spend exactly once against that record.
  18. Refresh state-only metadata after spend only when needed; never emit another accountable progress refresh after accounting.
  19. Report compactly.

This prompt is intentionally a lifecycle template. Scheduling policy lives in quota should-run.scheduler_hint, so per-project heartbeats, a shared controller loop, Codex CLI TUI, Claude Code loop, or future Codex goal-mode automations can all share the same LoopX quota guard without hard-coding different wait loops. Host implementations should first honor a terminal app_automation.host_action=pause_or_delete_current_heartbeat by stopping the current heartbeat once, verifying the result, and ending without scheduler ACK or quota spend. Otherwise they should read the compact app_automation.stateful_backoff packet, call automation_update only when apply_needed=true, and then let quota scheduler-ack-current persist the applied RRULE state from the latest scheduler hint without spending quota. A matching reset readback may instead set ack_needed=true; in that case skip the host write and execute the bound ack directly.