cyberine
UsageRun recipes

Edit pacing — a loop implementer writes before its round budget runs out

A key-only loop implementer (loop-deepseek-build and the other loop-*-build workers) used to spend every tool round reading and never call a write tool. When the budget ran out, the loop asked for a final answer with no tools, so the model could only describe the fix it had worked out (k7mvc, measured on deepseek-flash direct: 0 of 2 runs changed a file on a two-file task). LoopAgentAdapter now paces a run that is expected to change files. Three things happen, and only while no write tool (write_file / edit_file / multi_edit) has been called yet:

  • At run start, a system message states the round budget, says a described change is never applied, and names the round to start editing by.
  • After 40% of LOOP_MAX_TOOL_ROUNDS (round 10 of the default 25), a one-time user message says how many rounds are left and asks the model to stop surveying and start writing.
  • After 60% (round 15 of 25), reading tools are switched off: only the write tools are offered until one is called, and then every tool comes back so the model can check and correct its change.

After the change, the same task on the same model landed a typecheck-clean diff in 3 of 3 runs, and the one-line control task still passed.

Pacing applies only when the dispatcher sets Task.expectsEdits and the worker holds a write tool. The engine sets that flag wherever a worker runs in its own worktree and its diff is captured: isolate: true steps (the implement step of cyberine run with a build fleet, swarm chunks), kanban cards, beam-search candidates, swarm lanes, and the self-heal and integration-fix agents. Planning and review steps that reuse a build worker, judges, and the loop-*-direct compare workers are unchanged.

export LOOP_EDIT_PACING_PERCENT=40   # default: nudge at 40% of the round budget, write tools only from 60%
export LOOP_EDIT_PACING_PERCENT=0    # turn edit pacing off

The write-only point is always a third of the way from the nudge to the end of the budget. A budget too small to fit both steps gets only the nudge, or no pacing at all.

Known Limitations and Deferred Work (src/adapters/LoopAgentAdapter.ts).

  • Measured on one two-file task with one model (deepseek-flash over DeepSeek direct), 3 runs after the change. Other models and larger tasks are not measured.
  • A landed diff is not a complete one: in 1 of the 3 runs the first adapter edit failed on a non-unique old_string, and the run ended with only the parser half written. Pacing gets a write onto disk; it does not judge completeness.
  • The first write in a write-only round has to come from code the model already read. A failed write call still counts as a write and brings reading back.
  • The architect-editor editor task, a delegate_subtask subagent and any direct LoopAgentAdapter caller get no pacing unless they set Task.expectsEdits.
  • A write-only round sends a smaller tool list, which can miss a provider's prompt cache for that round.
  • A write through run_pipeline is detected by a plain substring match on its arguments.