feat(delegation): surface ready dependencies before the next decision - #76230
feat(delegation): surface ready dependencies before the next decision#76230Xipong wants to merge 4 commits into
Conversation
de7aeee to
adabcea
Compare
|
Thanks for separating the durable-event, routing, and carrier work into explicit commits. Problems
Suggested changes
Automated hermes-sweeper review. |
adabcea to
b23909f
Compare
Graph note (no action implied — a maintainer has already reviewed this thread). Our triage graph places this PR in a complex with 1 related pull request ( Full neighbourhood: https://hermes-triage.gottz.de/?node=76230 This note exists so the relationship stays discoverable from the thread itself. |
48e58c3 to
f959e9d
Compare
|
Rebased onto current Conflict in Verification: affected suite (10 files: async_delegation, delegation events, delegation delivery, apiserver background, completion delivery, tui gateway server, process registry, run_agent, cli async delivery, restored ownership) 922 passed / 1 failed. The single failure is upstream |
f959e9d to
7c73e26
Compare
|
Follow-up after CI: compacted the top-level description back to upstream's exact text (len(desc) = 1900 <= 2200, all required keywords retained). Per the compaction contract, the result_delivery details live in the parameter description instead of the top-level text. Verification after the fix:
PR description updated to the rebased commit SHAs (64edae26f + 7c73e26bc). |
7c73e26 to
3892f7c
Compare
|
Final semantic touch per review: the top-level description no longer promises "one consolidated message for a batch" — that phrase became false once children publish individually. The paragraph now reads "Result timing follows the result_delivery parameter", keeping the background/wait-or-poll anchors and the 2200-char compaction ceiling (len(desc) = 1852). Result timing details remain in the parameter description. Stack re-based onto the latest main (b3aa561); PR description SHAs updated (962bc39d8 / c5c767c89 / 9a2cb3f3a / 3892f7c24). |
930916c to
bae6a78
Compare
bae6a78 to
dd5a4ae
Compare
|
Rebase update: the stack is now rebuilt on current
There were relevant upstream changes since the previous documented base (
No upstream commit implemented the same Fresh local verification on this base before the CI refresh: #76228 32 passed; #76229 110 passed, 4 skipped; #76230 156 passed, 4 skipped; combined integration 1035 passed, 4 skipped. New CI runs are attached to the heads above. |
Important
This is the final behavior layer of a three-PR stack. It is cumulative over #76228 and #76229 because a fork branch cannot be used as the base of an upstream PR. The behavior owned by this PR is implemented by
9a6293b89plus the review-hardening fixdd5a4aef3; the stack map below separates the review boundaries.What this improves
Hermes can already run background subagents in parallel, but parallel execution is only useful when the result arrives while it can still affect the work that depends on it. Today, a reviewer, auditor, or prerequisite can finish during a long foreground turn yet remain invisible until that turn is over. By then the parent may already have made the edit, committed the change, or produced the final answer the child was meant to check.
This PR makes an explicitly dependent result visible at the next existing safe continuation boundary, while the parent still has an opportunity to use it. It turns background review from a post-hoc notification into actionable feedback without making the parent wait.
A concrete sequence is:
inject;Under
after_turn, step 4 happens only after the foreground workflow has ended. The bytes may eventually be identical, but their causal value is not: an audit received after the decision can only trigger rework, while the same audit received before the next decision can prevent the mistake.The practical benefits are:
Zero-setup dependency awareness
Most delegated dependencies in Hermes are ad hoc: review this edit, verify this assumption, inspect this failure, or check this plan. They are too small to justify user-managed project state or a separate orchestration setup, but timing still matters because the answer may invalidate what the parent is doing now.
Without a lightweight timing signal, the parent has two bad choices:
This PR removes that false choice inside the existing
delegate_taskUX. The parent marks the result as time-sensitive where it creates the work; Hermes then uses the existing delivery machinery to expose it at a useful boundary when possible. There is no new user-facing system to configure, no separate queue to operate, and no persistent planning workflow to maintain.The contract is bounded best effort, not a barrier:
Why this is a net improvement across model quality
Using timely evidence does not require a perfect planner; reacting to new tool evidence is already a core part of an agentic continuation. Straightforward results such as “this API assumption is false,” “the test fails at this boundary,” or “this edit violates the requested scope” can help any model that can use ordinary tool output. More capable models gain even more because they can synthesize nuanced independent review into their next decision instead of merely reading a post-mortem.
The upside is concrete:
after_turnremains the default and the parent need not opt in;A weaker model may still over-focus on injected evidence, spend effort reconciling an unimportant detail, or drift toward the child's framing. That is a quality-of-reasoning risk around one explicitly selected dependency, not a failure of delivery, history, or recovery. The downside is bounded by opt-in use and an unchanged default; the upside can avoid work that has already become wrong.
In other words, this PR adds a missing causal opportunity; it does not guarantee model judgment. Keeping all results after-turn because some models may use feedback poorly would also prevent stronger models—and weaker models in straightforward cases—from using timely, correct feedback at all. The safe policy is therefore opt-in current-turn eligibility with an unchanged default and durable fallback, not removing the opportunity globally.
Safe continuation shape
A ready dependent result is carried on the last new, unsent tool result of a complete tool-call batch. It is attached before that tail is first persisted. The next request therefore keeps the ordinary continuation shape:
There is no additional conversation message, no claimed user authorship, no rewritten history, and no manufactured model iteration. If that existing boundary does not occur before the foreground turn ends, the same durable event remains pending for the normal
after_turnpath.One delivery rail, better result timing
This is a UX improvement to the existing
delegate_taskflow, not a new task-management abstraction.result_deliveryonly records whether a delegation result is time-sensitive for the active work or can wait for the normal between-turn delivery:Both use the same:
The state machine remains shared:
For an
injectevent, the active loop merely gets the first bounded opportunity to claim it. If that opportunity is absent, late, lost, or rolled back, the event is still the same pending row consumed byafter_turn. There is no second queue, ledger, recovery scanner, or settlement protocol.Synthetic user vs. tool-boundary carrier
The old #74378 implementation did this after a tool block:
Even where a provider accepts
tool → user → assistant, that representation is wrong for Hermes: the user did not send the message, and the active turn did not end. It also forced the loop to handle a synthetic turn, provisional finalization, reconciliation grace, and provider-specific tail behavior.This PR does something narrower:
wait for a complete tool-call batch that already requires another model continuation;
inspect only the new, not-yet-persisted tool-result tail;
append a clearly delimited block beginning with:
persist that tool tail;
acknowledge the durable child row only after the transcript commit succeeds.
The provider necessarily receives the evidence inside tool-result content. The marker explicitly distinguishes that framework-added evidence from the bytes produced by the invoked tool and identifies its purpose. This reuses the same deliberate Hermes boundary as
/steer, which already appends clearly marked external input to the last tool result so the next assistant continuation can observe it without inserting a role. Tool output in Hermes can already contain framework-added truncation, guardrail, and steering context; this adds another explicitly identified framework block at that established seam.The restriction is intentional: if there is no complete tool batch, this PR does not fabricate a carrier. The event stays on
after_turn.How this addresses the #74378 close review
The close review identified real problems in that implementation. This redesign does not ask maintainers to relax those invariants.
role:"user"inside an active turnThe provider/cache concerns around the old shape were compatibility risks, not evidence that every major provider necessarily rejected it or that an already-cached prefix was rewritten. The decisive issue was Hermes' own turn/authorship invariant. The new carrier removes that issue rather than arguing for an exception.
The remaining product question is therefore precise:
It is no longer a question about permitting synthetic user turns.
Bounded semantic risk and unchanged baseline
The model-quality risk is deliberately contained at the policy surface:
after_turnremains the default and the interpretation of legacy or missing values;injectis explicit delegation metadata, chosen only when the result may change unfinished work;after_turndelivery;/goal, or routing heuristics.Existing goals, tool limits, iteration limits, compaction, and persistence still shape long-running agents, but they are surrounding capabilities—not scope added by this PR.
Bounded carrier storage
Delegation evidence uses the existing tool-result budget instead of bypassing it:
maybe_persist_tool_result(...)stores that report in the active environment and the carrier receives the ordinary<persisted-output>path plus bounded preview;after_turnrail.This keeps the child goal/context/summary available without allowing a large formatter result to inflate the next provider request after ordinary tool-output budgeting has already run.
Failure ordering
Carrier settlement is deliberately persistence-first:
If any operation after content mutation fails—including pending-claim registration or heartbeat startup—the exact original target structure is restored and every acquired durable claim is released/requeued. If persistence fails, the same rollback path removes the carrier before the event returns to pending. If the carrier was persisted, its durable event identity prevents restart recovery from presenting it twice. Private claim/display metadata is stripped before provider serialization.
Stack and review boundaries
778dcaab3b2c9c3e24after_turn; no active-loop behavior9a6293b89+dd5a4aef3Reviewing the cumulative branch commit-by-commit gives three ownership cuts. After the foundations merge, this PR can be rebased to show only
9a6293b89and its focused hardening commitdd5a4aef3.Compatibility
after_turn;/background: unchanged;after_turn;Verification
dd5a4aef3: 214 passed, 0 failed across carrier/ledger/storage, production compressor, CLI, gateway, TUI lifecycle, and executor paths;pending, unmodified, and at delivery attempt0;SessionDB, processed by the productionContextCompressor, and retained in the sanitized provider payload;dd5a4aef3: 28 successful, 9 skipped, 1 neutral, 0 failures; PRmergeable_state: clean;py_compile, andgit diff --check: passed;dd5a4aef3f7ae7cfb6473869c5f16c12b2a0e839.Related
Coordination graph
Hermes triage dashboard graph
Live dashboard neighbourhood: https://hermes-triage.gottz.de/?node=76230&complex=e394e6179542bacbd5e7dbdc5adc9dfb
flowchart LR classDef focus fill:#fef3c7,stroke:#b45309,stroke-width:3px,color:#451a03 classDef issue fill:#ede9fe,stroke:#6d28d9,color:#2e1065 classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937 P76230["PR #76230 (open)"] I85648(["issue #85648 (open)"]) P58690["PR #58690 (closed)"] P60863["PR #60863 (merged)"] P61332["PR #61332 (open)"] P62608["PR #62608 (open)"] P63866["PR #63866 (closed)"] P65860["PR #65860 (open)"] P70133["PR #70133 (merged)"] P72596["PR #72596 (open)"] P74378["PR #74378 (closed)"] P76229["PR #76229 (open)"] P76230 -->|related| I85648 P76230 -->|closes| I85648 P74378 -.->|duplicate of 0.76| P76230 P76229 -.->|duplicate of 0.67| P76230 P58690 -.->|duplicate of 0.66| P76230 P63866 -.->|duplicate of 0.66| P76230 P60863 -.->|duplicate of 0.66| P76230 P65860 -.->|duplicate of 0.65| P76230 P72596 -.->|duplicate of 0.65| P76230 P61332 -.->|duplicate of 0.65| P76230 P70133 -.->|duplicate of 0.65| P76230 P62608 -.->|duplicate of 0.64| P76230 class P76230 focus click P76230 "https://github.com/NousResearch/hermes-agent/pull/76230" class I85648 issue click I85648 "https://github.com/NousResearch/hermes-agent/issues/85648" class P58690 closed click P58690 "https://github.com/NousResearch/hermes-agent/pull/58690" class P60863 merged click P60863 "https://github.com/NousResearch/hermes-agent/pull/60863" class P61332 open click P61332 "https://github.com/NousResearch/hermes-agent/pull/61332" class P62608 open click P62608 "https://github.com/NousResearch/hermes-agent/pull/62608" class P63866 closed click P63866 "https://github.com/NousResearch/hermes-agent/pull/63866" class P65860 open click P65860 "https://github.com/NousResearch/hermes-agent/pull/65860" class P70133 merged click P70133 "https://github.com/NousResearch/hermes-agent/pull/70133" class P72596 open click P72596 "https://github.com/NousResearch/hermes-agent/pull/72596" class P74378 closed click P74378 "https://github.com/NousResearch/hermes-agent/pull/74378" class P76229 open click P76229 "https://github.com/NousResearch/hermes-agent/pull/76229"Dashboard interpretation:
closes/related);0.88and file overlap0.75;duplicate ofedge below the fold threshold is a similarity lead for review, not an accepted duplicate or merge-order edge;This graph is additive to the hand-audited coordination block above: the dashboard supplies discovery neighbourhoods, while the declared dependency, collision, ownership, and merge-order edges remain the reviewed coordination contract.