Skip to content
23 changes: 15 additions & 8 deletions docs/heartbeat-automation-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ 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,
Expand All @@ -54,7 +61,7 @@ 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 budget remains 3,500 characters.
settlement or terminal vision closure. The brief budget is 4,300 characters.

Brief 与 thin 共用完整执行义务路径;这次有意移除 brief 固定结算配方,而不是
删除结算义务。真实 App preflight、registry scope、完整 guard 和静态安全规则
Expand Down Expand Up @@ -429,17 +436,17 @@ If the result says should_run=false:
user_todo_summary, and agent_todo_summary from the payload. If the same
unresolved gate has not already been asked in the recent visible thread,
return heartbeat NOTIFY with
one concise Chinese question that lists the gate and the expected reply
format. Treat `interaction_contract.user_channel.notify` as the final
one concise question in the user's language that lists the gate and the
expected reply format. Treat `interaction_contract.user_channel.notify` as the final
notification signal. When it is `NOTIFY`, name concrete projected
`actions`, todos, or questions even when `action_required=false`,
`user_todo_summary.open_count=0`, and `non_blocking=true`; non-blocking means
the agent may continue independent work, not that the user action is silent.
Never say only "owner gate". If required user-facing items are not projected,
say "具体 user todo 未投影,需修复 LoopX 状态投影"; never say "no new user
action" for this case. Only when `notify=DONT_NOTIFY`,
say "specific user Todo is not projected; repair LoopX state projection" in
the user's language; never say "no new user action" for this case. Only when `notify=DONT_NOTIFY`,
`action_required=false`, and `open_count=0` may the heartbeat say
"无用户待办/无需通知" or stay quiet. Do not execute agent_command, adapter
"no user action required" in the user's language or stay quiet. Do not execute agent_command, adapter
work, write-control, production actions, or the gated path while asking.
- If the payload says notify_user_on_open_todo=true, treat the existing open
user_todo_summary as a blocker-push opportunity, not as a silent skip. This
Expand All @@ -451,8 +458,8 @@ If the result says should_run=false:
user_gate_notification_cooldown.notification_suppressed=true, preserve the
pending gate but return quiet DONT_NOTIFY until its bounded reminder window
or a material gate/host change. Otherwise, if the same blocker ask has not already been surfaced in
the recent visible thread, return heartbeat NOTIFY with one concise Chinese
ask listing at most three first_open_items, the open_todo_notify_reason, and
the recent visible thread, return heartbeat NOTIFY with one concise ask in
the user's language listing at most three first_open_items, the open_todo_notify_reason, and
the expected reply format: done, defer/not now, or a new evidence
link/date/conclusion. Do not do implementation work, adapter work, file
edits, research, project exploration, or quota spend for that blocker-push
Expand Down
35 changes: 28 additions & 7 deletions docs/reference/contracts/interface-budget-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ and size/count budgets.

| Surface | Owner | Consumer Action | Cold Path | Size Budget | Nested Budget | Count Budget |
| --- | --- | --- | --- | --- | --- | --- |
| `heartbeat_prompt_json` | heartbeat automation | wake and route one bounded turn | `quota should-run`, `status`, or `review-packet --handoff-only` | `json_chars <= 4800` plus `interface_budget.within_budget=true` | `nested_keys <= 40` | `top_level_keys <= 30` |
| `heartbeat_prompt_json` | heartbeat automation | wake and route one bounded turn | `quota should-run`, `status`, or `review-packet --handoff-only` | `json_chars <= 5400` plus `interface_budget.within_budget=true` | `nested_keys <= 40` | `top_level_keys <= 30` |
| `review_packet_handoff_only_json` | project-agent handoff | forward the smallest sufficient task packet | full `review-packet` or run-history artifact | `json_chars <= 3000` plus `handoff_interface_budget.within_budget=true` | `nested_keys <= 40` | `top_level_keys <= 18` |
| `quota_should_run_json` | quota guard | decide whether the selected goal may spend compute | `status`, `history`, or active state | `json_chars <= 14500` | `nested_keys <= 360` | `top_level_keys <= 52` |
| `dashboard_status_json` | operator dashboard | render first-screen operator state | `history`, run artifacts, or project-local adapter output | `json_chars <= 19500` | `nested_keys <= 260` | `top_level_keys <= 25` |
Expand All @@ -35,17 +35,25 @@ for the richer generator packet; neither is the recurring Agent hot path.

The heartbeat envelope ceiling covers the unbound and representative agent/scope-bound
Codex App thin fixtures. It includes generator metadata and repeated bound commands,
not only the execution prompt. The shared host contract added static safety, repair
routing, and retry-stable Turn initialization; the scoped fixture now uses about
4,362 JSON characters. The 4,800-character ceiling leaves roughly 10% headroom for
that fixture, without relaxing the independent **2,500-character thin task body**,
not only the execution prompt. On the same scoped fixture, current main uses
4,791 JSON characters and the language-aware body uses 5,167 while retaining
static safety, repair routing, work obligation, and settlement instructions.
The 5,400-character ceiling leaves 233 characters of fixture headroom, without
relaxing the independent **2,500-character thin task body**,
4,000-character native Goal body, structural limits, or emitted CLI ceilings.
It is not a token count, execution quota, or allowance to append more instructions.
Arbitrary-length caller paths/scopes are not promised to fit this fixed fixture
envelope; their emitted output is qualified separately by the CLI matrix.
Do not remove safety or settlement semantics to fit the envelope, and do not copy
dynamic quota decisions into the static prompt. No prompt text, saved automation,
scheduler cadence, or spending policy changes with this qualification adjustment.
dynamic quota decisions into the static prompt. The brief body allowance rises
from 3,500 to 4,300 characters. Translating its fixed Chinese instructions to
English grows the Codex App brief body from 3,282 to 3,980 characters (3,494 to
4,192 with two agent-profile scopes) while its `o200k_base` token count falls
from 962 to 927 (994 to 959) and UTF-8 bytes grow about 2%. The character
ceiling therefore moves with the script, not with prompt cost, and keeps about
8% headroom for the unscoped fixture, close to the previous 7%. Saved
automation, scheduler cadence, and spending policy do not change with these
budget adjustments.

The quota budget includes the typed action portfolio, one shared bound CLI
route, pending-selection qualification, and hard-lane preemption evidence. The
Expand Down Expand Up @@ -102,6 +110,19 @@ packet, and the brief/compact/full heartbeat prompt modes. These remain opt-in
cold paths, but their exact stdout size and semantic anchors are regression
contracts too.

The user-language prompt transition is one measured exception to ordinary
base/head growth, scoped to heartbeat rows and only when the base lacks the
rendered language-policy revision. On the same small CLI fixture, `origin/main`
to this branch grew by 376 characters for thin, 695 JSON / 690 Markdown
characters and five Markdown lines for brief, 264 / 262 characters for
compact, and 258 / 260 for full. Replacing fixed Chinese instructions and
restoring blocker/next-action continuation gives the worker usable language
and work guidance; removing those clauses solely to fit the old delta would
lose that consumer value. The one-time per-mode allowances are 400, 720, 288,
and 288 characters respectively, plus six lines for brief. The absolute
surface ceilings, UTF-8 byte limits, quota/status budgets, and normal growth
limits after this revision becomes the baseline remain unchanged.

`todo list --thin` is an explicit bounded projection, not a new filtering or
ordering mode. After the normal role, status, Todo-id, and agent filters run,
it keeps at most two matched items per role in one top-level `todos` container.
Expand Down
8 changes: 4 additions & 4 deletions examples/blocker-push-runtime-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,9 @@ def main() -> int:
assert "state=operator_gate" not in compact_prompt, prompt
assert "Normal turns use CLI `interaction_contract`" in compact_prompt, prompt
assert "`user_channel.notify` controls OUTPUT only" in compact_prompt, prompt
assert "NOTIFY=向用户输出动作; DONT_NOTIFY=安静输出" in compact_prompt, prompt
assert "Due/peer非用户动作" in compact_prompt, prompt
assert "NOTIFY缺动作→具体user todo未投影" in compact_prompt, prompt
assert "NOTIFY=show; DONT_NOTIFY=no output" in compact_prompt, prompt
assert "Due/peer work is not a user prompt" in compact_prompt, prompt
assert "Missing NOTIFY action: user Todo unprojected" in compact_prompt, prompt
# The bootstrap rule is shared from heartbeat.rules after #4201; assert the
# current compact sentence instead of the retired per-shell phrasing.
assert "reuse the value on retries" in compact_prompt, prompt
Expand All @@ -222,7 +222,7 @@ def main() -> int:
# The shipped writeback sentence moved to the mixed-language form in the
# same change that updated examples/control_plane/heartbeat-prompt-smoke.py;
# this assertion keeps the blocker-runtime path pinned to the same text.
assert "unchanged→真实--vision-unchanged-reason" in compact_prompt, prompt
assert "unchanged->truthful --vision-unchanged-reason" in compact_prompt, prompt

print("blocker-push-runtime-smoke ok")
return 0
Expand Down
3 changes: 3 additions & 0 deletions examples/control_plane/cli-output-probe-runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ def _receipt_row(
semantics.runtime_root_command_route_count(text)
),
"host_prompt_static_safety_revision": semantics.host_prompt_static_safety_revision(text),
"heartbeat_user_language_prompt_revision": (
semantics.heartbeat_user_language_prompt_revision(text)
),
"reward_memory_outcome_prompt_revision": (
semantics.reward_memory_outcome_prompt_revision(text)
),
Expand Down
Loading
Loading