Skip to content

[codex] Add progress outcome canary - #35702

Open
OmarB97 wants to merge 1 commit into
NousResearch:mainfrom
OmarB97:fix/progress-outcome-canary
Open

[codex] Add progress outcome canary#35702
OmarB97 wants to merge 1 commit into
NousResearch:mainfrom
OmarB97:fix/progress-outcome-canary

Conversation

@OmarB97

@OmarB97 OmarB97 commented May 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a turn-level progress outcome canary that watches tool rounds, not just individual tool families
  • inject one synthetic progress nudge after repeated tool rounds with no observable work outcome
  • record progress canary metadata in the run result and logs, while staying quiet after landed file mutations or state-changing commands

Root Cause

The search/read/terminal guardrails catch known local loop shapes, but an agent can still burn several tool rounds doing plausible-looking diagnostics without producing a concrete outcome. There was no higher-level assertion that a turn had made an edit, changed durable state, opened/updated workflow state, or deliberately changed strategy.

Validation

  • ./scripts/run_tests.sh tests/run_agent/test_tool_call_guardrail_runtime.py tests/agent/test_tool_guardrails.py tests/tools/test_read_loop_detection.py
  • python -m ruff check agent/progress_outcome.py agent/agent_init.py agent/conversation_loop.py run_agent.py tests/run_agent/test_tool_call_guardrail_runtime.py
  • git -c core.fsmonitor=false diff --check

Fork Mirror

@daimon-nous daimon-nous Bot added type/feature New feature or request P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels May 31, 2026
@OmarB97
OmarB97 marked this pull request as ready for review May 31, 2026 05:18
@OmarB97
OmarB97 force-pushed the fix/progress-outcome-canary branch from 10b1a69 to 92e9fe7 Compare June 10, 2026 00:10
@OmarB97
OmarB97 force-pushed the fix/progress-outcome-canary branch from 92e9fe7 to 8e7d34c Compare June 11, 2026 17:45
@OmarB97
OmarB97 force-pushed the fix/progress-outcome-canary branch from 8e7d34c to 4eaf17e Compare June 11, 2026 17:52
@OmarB97
OmarB97 force-pushed the fix/progress-outcome-canary branch from 4eaf17e to 2fd7b8f Compare June 11, 2026 17:53
@OmarB97
OmarB97 force-pushed the fix/progress-outcome-canary branch from 2fd7b8f to a6e17f2 Compare June 11, 2026 17:59
@OmarB97
OmarB97 force-pushed the fix/progress-outcome-canary branch from a6e17f2 to fd0dfee Compare June 11, 2026 18:14
@OmarB97
OmarB97 force-pushed the fix/progress-outcome-canary branch from fd0dfee to 33d9162 Compare June 11, 2026 18:20
@OmarB97

OmarB97 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Rebased this upstream mirror onto current main and repaired the CI fallout. Current head: fd0dfeebb was superseded by 33d9162b0, final head is 33d9162b0527d6593d69b209fb0ea567238fa4e7. Local verification passed: scripts/run_tests.sh tests/test_run_tests_parallel.py tests/run_agent/test_vision_tool_messages.py tests/agent/test_local_stream_timeout.py tests/run_agent/test_streaming.py tests/tools/test_file_tools.py -- -q plus py_compile/diff-check on the touched runner/Docker tests. GitHub is now CLEAN/MERGEABLE with all checks green, including all six test shards, typechecks, Nix, Docker amd64/arm64, lint, attribution, and common-ancestor. Merge attempt through meshctl pr merge was blocked only by GitHub permission: OmarB97 cannot execute MergePullRequest on this upstream repo.

@OmarB97

OmarB97 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

Refreshed onto upstream/main (rebase, live merge-base c9a150d640). One conflict: agent/chat_completion_helpers.py had an add/add collision where upstream's new cross-turn stale-call circuit breaker (_stale_streak/_check_stale_giveup, #58962) and this PR's new resolve_stream_stale_timeout/dflash-timeout helpers both inserted at the same blank-line location — resolved by keeping both blocks side by side (no logic overlap; verified call sites for both features are still wired correctly through interruptible_api_call/interruptible_streaming_api_call). Original head 991aebddbb -> refreshed 113c3406b1. Content unchanged vs original diff (33 files, +3370/-88 vs original +3372/-88 — the 2-line delta is dedup of the conflict-marker region itself). Focused tests green: test_stall_retry.py + test_chat_final_response_resilience.py (16 passed). Checks re-running.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for targeting the remaining gap between per-tool repetition checks and a turn-level lack of outcome. The premise remains present on current main: agent/tool_guardrails.py:350-375 only tracks repeated results for the same idempotent call signature.

Problems

  • agent/conversation_loop.py:4888-4892 appends a synthetic user message after tool execution. This violates the strict alternation invariant in AGENTS.md:88-91, which explicitly prohibits synthetic user messages mid-loop.
  • agent/stall_retry.py:271-272 independently constructs a synthetic assistant/user pair for retry, creating the same invalid in-turn transcript shape.
  • agent/stall_retry.py:20-28 adds user-facing non-secret HERMES_STALL_RETRY_* settings; AGENTS.md:102-107 requires behavioral settings to use config.yaml instead.
  • The 33-file diff also includes dflash retry/model switching, result compaction, timeout, MeshBoard routing, and file-tool work beyond the stated canary.

Suggested changes

  • Attach the canary guidance to an existing tool result instead of appending a user message, and add an end-to-end role-alternation test.
  • Remove or separately redesign the retry transcript mutation.
  • Keep user-facing settings in config.yaml and split unrelated feature work from the canary.

Automated hermes-sweeper review.

)
if _progress_decision is not None:
agent._emit_status("Progress canary nudged the model to pick a concrete outcome")
messages.append({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appends a synthetic user turn after a tool batch in the active loop. AGENTS.md:88-91 explicitly prohibits synthetic user messages mid-loop to preserve role alternation and prompt-cache invariants. Attach the advisory to an existing tool-result message instead, and cover the resulting transcript shape end to end.

Comment thread agent/stall_retry.py
visible = (stalled_content or "").strip()
if visible:
retry_messages.append({"role": "assistant", "content": visible})
retry_messages.append({"role": "user", "content": _STALL_RETRY_NUDGE})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This retry path independently creates a synthetic user turn in the current conversation payload. It has the same role-alternation problem as the canary injection; a retry mechanism must preserve the transcript roles rather than append an assistant/user pair mid-turn.

Comment thread agent/stall_retry.py
(e.g. ``qwen3.6-27b-256k``). Default-off => zero change to existing behavior.

Env:
HERMES_STALL_RETRY_MODEL retry lane/model name (required to enable)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are user-facing non-secret behavior controls. Project policy requires timeouts, thresholds, flags, and similar settings to live in config.yaml, not new HERMES_* environment variables. Keep the config mapping and remove these env overrides.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) labels Jul 13, 2026
@teknium1 teknium1 added the sweeper:blast-massive Sweeper blast radius: massive — everyone, every turn (invariant surface) label Jul 13, 2026
Refresh fix/progress-outcome-canary onto current upstream/main and
reconcile conflicts across the local-model feature stack:

- chat_completion_helpers: adopt the resolve_stream_stale_timeout()
  extraction but re-apply upstream's reasoning-model stale-timeout floor
  via max() at the call site (the extraction omits it); union the
  anthropic/openai stale-stream client rebuild with the no-chunk abort
  join + TimeoutError break.
- turn_context/turn_finalizer: union the progress-outcome canary + stall
  retry summary hooks with upstream's consolidation-reset/cleanup-errors.
- runtime_provider: carry the HERMES_LLM_BASE_URL stream-tap fallback
  through upstream's os.getenv -> _getenv (profile-scoped) migration.
- file_tools search: merge the empty-result _hint with upstream's
  file_read/densify redaction + credential-omission _omitted block.
- conversation_loop: stall-retry failure returns now surface the block's
  error text as final_response (satisfies the run_conversation
  final_response contract instead of literal None).
- Drop the release AUTHOR_MAP duplicate and the skip_missing runner
  machinery whose _run_one_file support was removed upstream.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:blast-massive Sweeper blast radius: massive — everyone, every turn (invariant surface) sweeper:risk-caching Sweeper risk: may break/degrade prompt caching or cache-key stability (invariant) sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants