feat: enforced goal execution — hard pivot enforcement, semantic loop detection, verification gates - #22890
feat: enforced goal execution — hard pivot enforcement, semantic loop detection, verification gates#22890FlowGodPR wants to merge 5 commits into
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the detailed goal-execution design and tests. This needs substantial salvage work against current main rather than a direct replacement.
Problems
- The PR changes
GoalManager.evaluate_after_turn()to removebackground_processes(hermes_cli/goals.py:421-427in the PR), but current CLI, gateway, and TUI callers still supply it (cli.py:9276-9280,gateway/run.py:12707-12711,tui_gateway/server.py:9178-9182). That would raiseTypeErroron active goal continuations. - Semantic loop detection reads
tool_calls(hermes_cli/goal_judge.py:437-461in the PR), but none of those live callers capture or pass tool calls, so the detector receives[]. - The proposed verification gate checks scratchpad artifacts (
hermes_cli/goals.py:490-497), but production code does not populate or verify them. High-confidence DONE verdicts would therefore be downgraded without a completion path. - Current main already has completion-contract evidence checks and wait barriers in
hermes_cli/goals.py:190-214and1363-1485; the PR replacement removes their associated API surface.
Suggested changes
- Rework this as an additive integration on current
GoalManager, retaining contracts, subgoals, wait barriers, migration, and kanban exports. - Wire real tool-call/result evidence through every continuation surface before enforcing loop or artifact decisions.
Automated hermes-sweeper review.
| def evaluate_after_turn( | ||
| self, | ||
| last_response: str, | ||
| tool_calls: Optional[List[Dict[str, Any]]] = None, |
There was a problem hiding this comment.
Current CLI, gateway, and TUI callers pass background_processes= to this method. Removing that keyword makes every live /goal continuation raise TypeError; retain the parameter and integrate the current wait-barrier behavior before adding tool-call input.
| data = json.loads(text) | ||
| except Exception: | ||
| m = re.search(r"\{.*?\}", text, re.DOTALL) | ||
| data = json.loads(m.group(0)) if m else {} |
There was a problem hiding this comment.
This fallback json.loads() is unguarded. A malformed auxiliary decomposition reply will escape decompose_goal() as JSONDecodeError rather than returning [], potentially breaking /goal setup.
| ) | ||
|
|
||
| # ── Pre-processing: semantic loop & error detection ────────── | ||
| is_semantic, sem_desc = _detect_semantic_loop(tool_calls or []) |
There was a problem hiding this comment.
The existing CLI, gateway, and TUI continuation paths do not pass tool_calls, so this detector always sees [] in production. Wire actual per-turn tool activity from all three drivers before using it for an enforced pivot.
| # ── Terminal actions ───────────────────────────────────── | ||
| if verdict.action == "done": | ||
| # Final verification gate | ||
| verified = sum(1 for a in self._scratchpad.artifacts if a.verified) |
There was a problem hiding this comment.
No production path in this PR populates or verifies self._scratchpad.artifacts; only tests insert them directly. This gate therefore prevents normal goals from reaching done until artifact extraction and verification are wired to real tool results.
GottZ
left a comment
There was a problem hiding this comment.
This was generated by AI during triage.
Summary
Two PRs address this complex: #22858 audits and corrects documentation against live registries and runtime behavior, while #22890 includes the same documentation patch plus a large replacement of the persistent-goal execution engine. The goal-engine diff targets loop detection, pivot enforcement, adaptive budgets, and completion verification, but its proposed data and API paths are not wired into the current callers.
Related pull requests
- #22858 [merged]
related— (+132/-89) — merged reference implementation: this documentation audit corrects CLI flags, configuration keys, provider/runtime descriptions, messaging setup, and other operational guidance; it remains relevant because #22890 carries substantially the same documentation changes. - #22890
related— (+2355/-767) — author action required despite the keep_open review on #22890: the contributor review identifies that the diff removes thebackground_processesparameter while live CLI, gateway, and TUI callers still pass it, expectstool_callsthat those callers do not supply, and gates completion on scratchpad artifacts that production code does not populate. Its loop-detection, scratchpad, and goal-judge work may be salvageable, but the current replacement also overlaps documentation already merged through #22858 and removes current-main completion-contract evidence checks and wait barriers.
Duplicates
#22858 and #22890 contain substantially the same documentation audit; that portion of #22890 is duplicate of the merged #22858, while #22890's goal-execution implementation is distinct.
Suggested consolidation
Author action: rebase #22890 onto main, or split out the part that can merge. Preserve the keep_open review's salvage path by isolating the goal-judge, scratchpad, or loop-detection pieces that can be wired into current callers without removing existing completion-contract and wait-barrier behavior; drop the documentation changes already merged in #22858, and explicitly restore or migrate the background_processes API, tool-call capture, and artifact-population paths before further review.
Cross-PR triage: Reviewed 2 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 225 kB of PR diffs, 10 kB of issue/PR text, 2 kB of discussion (1 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.
Adds semantic loop detection, artifact extraction, verification gates,
goal decomposition (DAG), and adaptive budget — additively layered on
top of the existing GoalManager without changing a single existing line.
Architecture (purely additive — every existing contract preserved):
- New files: goal_judge.py (multi-dim judge), goal_scratchpad.py (DAG memory)
- goals.py: New functions + GoalManager methods, existing code UNTOUCHED
- Callers: evaluate_after_turn_enhanced wraps the existing judge
Changes:
1. New goal_judge.py (616 lines) — semantic loop/intent detection,
calibrated completion bands (0-1.0), quality scoring, error pattern
tracking, negative constraints, hard enforcement overrides
2. New goal_scratchpad.py (545 lines) — DAG-aware sub-task tracking,
artifact registry, error/constraint/approach history, dependency
inference, parallel batch computation
3. goals.py (+295 lines additive):
- Enhanced constants + import gates (no crash if modules missing)
- decompose_goal() — LLM-based sub-task decomposition with deps
- estimate_enhanced_budget() — complexity-scaled (5-200 turns)
- build_continuation_prompt() — rich context with constraints/errors
- GoalManager._extract_artifacts_from_turn() — auto-detect writes
- GoalManager.evaluate_after_turn_enhanced() — wraps basic judge,
adds loop detection, artifact verification gate, scratchpad tracking
- GoalManager._scratchpad — fresh per session, optional
- __all__ exports new types
4. CLI/Gateway/TUI — call evaluate_after_turn_enhanced with tool_calls
extracted from conversation history; background_processes preserved
Preserved (100% intact):
- background_processes parameter and wait barriers
- Subgoals (/subgoal) — fully functional
- Completion contracts (GoalContract) — fully functional
- Kanban goal loop — fully functional
- Judge parse/transport failure auto-pause — fully functional
- Goal persistence (SessionDB state_meta) — fully compatible
- All 36 existing tests pass with zero modifications
Addressed review (teknium1):
1. background_processes= parameter — preserved (upstream already had it)
2. Unguarded json.loads — fixed in decompose_goal fallback
3. tool_calls always [] — wired from all 3 callers via history
4. Artifact gate blocking goals — _extract_artifacts_from_turn + gate
only fires when artifacts exist but none verified
Addressed review (GottZ):
- Rebased onto current upstream/main — zero diff against upstream
- Dropped all duplicate documentation changes (already in NousResearch#22858)
- Additive only — no existing code removed, no contracts broken
- goal_judge/scratchpad isolated as new files with clear API surface
- Route decompose_goal through call_llm(task='goal_judge') for consistent auxiliary.goal_judge.* config path - Add _DECOMPOSE_SYSTEM_PROMPT constant - Add scratchpad persistence: _save_scratchpad, _load_scratchpad_from_db, _delete_scratchpad with separate meta key - Wire scratchpad into set(), evaluate_after_turn_enhanced - Migrate scratchpad in migrate_goal_to_session for context compression - 16 new tests (52 total): budget estimation, artifact extraction, decompose, enhanced eval integration, scratchpad persistence All green (52/52 passed)
- evaluate_turn now uses call_llm(task='goal_judge', ...) instead of direct get_text_auxiliary_client + client.create — consistent with the existing judge_goal, respects auxiliary.goal_judge.* config - verify_artifact now stats the file on disk before marking verified, so the verification gate can actually complete - All 52 tests pass
- Verification gate now calls verify_artifact(path) on every artifact during 'done' verdict — checks file existence on disk - Existing files pass the gate; missing files trigger refine_output - No more deadlock: the gate can actually complete - 2 new tests proving disk-stat behavior and gate auto-stats
- goal_judge.py: 16/15 functions with docstrings, every public and private function has Args/Returns - goal_scratchpad.py: 45/41 functions with docstrings, all mutation helpers, properties, and serialization documented - goals.py: enhanced docstrings on 4 additive functions with full pipeline descriptions and parameter documentation - All 54 tests pass, zero regressions
c88e423 to
eed9ccf
Compare
Full rework per review feedback — additive integration on current mainThis PR was completely rebuilt to address both reviews. It is now purely additive: 7 files, +2,377 / −3. Every existing contract is preserved. What changed vs. the previous revision
Reviewer comments addressed
New integration details
Tests54/54 passing (non-integration): 36 existing + 18 new covering artifact extraction, budget estimation, goal decomposition, enhanced evaluation integration, scratchpad persistence, and disk-stat verification. uv run pytest tests/hermes_cli/test_goals.py -o "addopts=-m 'not integration'"Ready for re-review. |
Rebuilds the persistent goal execution engine with a three-stage evaluation pipeline that adds deterministic pre-processing and hard enforcement around the existing LLM judge.
Motivation
Stock
/goaluses a binary judge to decide if a goal is done. Three problems emerge from this architecture in practice:First, the judge is advisory. When it recommends a pivot or a refinement, the agent can ignore it and keep retrying the same failing pattern. There is no mechanism to say "no, you actually have to change course."
Second, completion is self-reported. The judge asks "are you done?" and the agent says "yes." No one checks whether the output actually exists, works, or passes any form of validation.
Third, each turn is independent. A command that fails on turn 3 will be retried on turns 4, 5, and 6 because nothing tracks what has already been tried and failed.
How it works
Each turn now runs through three stages:
Pre-processing runs deterministic checks before the LLM judge is called. It classifies every tool call by intent — installing packages, making HTTP requests, reading files — and flags when the same intent repeats. It also tracks error patterns across turns and compares the last several completion scores to detect regression. These are computed, not inferred by an LLM.
The LLM judge then evaluates the turn as before, but with richer context: the pre-processed signals, a summary of scratchpad state (sub-tasks completed, artifacts created, approaches tried), and a calibrated scoring rubric with concrete examples for each completion band.
Post-processing enforces the results. If pre-processing detected a loop or regression and the judge returned anything other than a pivot, the verdict is overridden to force a pivot. Completion scores above 0.75 are capped unless at least one artifact has been marked as verified. A goal is only accepted as "done" when completion reaches 0.91, quality reaches 0.70, at least one artifact is confirmed, and the agent has explicitly stated completion rather than "next I'll do X."
When a pivot fires, a negative constraint is generated ("do not retry the same install pattern") and persisted across all future turns of that goal. The scratchpad now carries dependency edges between sub-tasks, enabling parallel dispatch when tasks are independent, and maintains a history of verdicts for trend detection.
The adaptive budget scales from 5 to 200 turns based on goal complexity and auto-extends when the agent is more than halfway done and moving forward.
What's new and what's next
The pipeline pattern — deterministic pre-processing that can override the LLM — is the core architectural contribution. The semantic loop detection (classifying by intent rather than exact string match) is a concrete improvement over existing approaches. The verification gate and negative constraints are directionally correct and functional, though a proper implementation would add programmatic verification rather than relying on the agent's self-report, and tool-call interception rather than prompt injection for constraint enforcement.
The implementation is a drop-in replacement. The public API of
GoalManageris unchanged. Active goals survive the upgrade. No config changes are required. 39 tests pass.Files
hermes_cli/goal_judge.py(new): Multi-dimensional evaluation engine with semantic loop detection and calibrated scoringhermes_cli/goal_scratchpad.py(new): Working memory with dependency edges, error tracking, negative constraints, and verdict historyhermes_cli/goals.py: Orchestrator rewritten with the three-stage pipeline, hard enforcement, and adaptive budgettests/hermes_cli/test_goals.py: 39 tests covering loops, gates, DAG, budgets, and constraintsdocs/enforced-goal-execution.md: Full design documentdocs/enforced-goal-benchmarks.md: Architectural comparison against stockwebsite/docs/user-guide/features/goals.md: Updated user documentation