Skip to content

feat(autopilot): engine-enforced goal-chasing with a Council-or-fallback reviewer + decision log - #49917

Closed
arminanton wants to merge 13 commits into
NousResearch:mainfrom
arminanton:fix/tui-notify-autodispatch-gate
Closed

feat(autopilot): engine-enforced goal-chasing with a Council-or-fallback reviewer + decision log#49917
arminanton wants to merge 13 commits into
NousResearch:mainfrom
arminanton:fix/tui-notify-autodispatch-gate

Conversation

@arminanton

@arminanton arminanton commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

What /autopilot does

Autopilot is engine-enforced goal-chasing: tell it a goal and it keeps working until the goal is verifiably complete, instead of stopping at the first plausible "I think that's done." The mechanism that makes this trustworthy is an independent reviewer at every decision point. The agent never grades its own homework. A separate judge decides "is the goal genuinely complete, or must I keep going?", and auto-answers any clarify question with the most-defensible choice so an unattended run never dead-ends on a 3am "should I continue?" prompt.

This PR completes that design in two ways: it makes autopilot fully usable without the Hermes Council (so anyone can adopt it), and it gives every run a reviewable decision trail.

The reviewer: Hermes Council (preferred) with a graceful fallback

The preferred reviewer is the Hermes Council, and it is worth explaining why, because the Council is the part that makes autonomous loops actually reliable rather than fragile.

A single model reviewing its own work tends to agree with itself. That self-agreement is exactly what lets a lazy "done" slip through and what makes naive agent loops either stop too early or loop forever. The Council replaces that with structured disagreement: multiple independent critic personas that are explicitly told not to balance or be agreeable, and a separate Arbiter that reads their raw outputs as data, separates "what the user wants" from "what the evidence supports" from "the safest reversible path," and applies an accuracy ceiling that caps confidence when evidence is thin or the critics disagree. For a completion gate this is the right axis to optimize: the Skeptic's whole job is to find why the work is not done, so a sycophantic "complete" has to survive an adversarial pass before autopilot is allowed to stop.

The genuinely novel part, and the reason this is worth adopting even on a single subscription: the Council runs all of this from one model. You don't need multiple providers or API keys. It convenes 24 anti-sycophancy personas as separated roles/profiles over whatever model Hermes already has configured (Copilot, Codex, OpenRouter, direct keys) via its Hermes-native in-process lane. Multi-provider diversity is an optional enhancement, not a requirement. There's a durable daemon that owns each run (re-attachable, single-flight dedup, self-recovery reaper with per-call wall-clock bounds), anonymous peer review (Borda ranking + a disagreement map), and a sycophancy audit that can force a Devil's-Advocate re-deliberation. If you want the strongest version of autopilot, run the Council:

https://github.com/arminanton/hermes-council

A note for anyone who tried the earlier hermes-council and moved on: this is not that project. It's a ground-up rebuild that keeps the original MCP contract (same tools, fast/standard/deep modes, structured verdicts) but is a different engine underneath. The original shipped 5 personas as a pip package; this one is a self-contained project with 24 personas across 9 bounded panels and 6 presets, an engine-enforced accuracy ceiling, anonymous peer review, a sycophancy audit, a durable re-attachable daemon with per-call wall-clock bounds, multi-lane providers (hermes-native in-process, codex-cli, copilot-cli, gemini-cli, offline), and a benchmark harness (Council-vs-single-model on a planted-flaw dataset). If your mental model of "hermes-council" is the old 5-persona version, the behavior here is materially different. Worth a fresh look.

Point Hermes at it with COUNCIL_PROVIDER=hermes and it reuses your existing model and auth; no extra subscription.

When the Council is not installed, autopilot degrades cleanly: first to a single independent auxiliary-model reviewer pass (still not the main model grading itself), and finally fails open (stop) rather than looping blindly. This PR adds the missing piece of that fallback so it matches the documented design: when there's no Council, the clarify auto-answer now surfaces the full set of options it weighed and the single recommended pick (via choose_answer_detailed), rather than choosing silently, and labels which reviewer produced the decision.

The memory that makes long runs survivable: hermes-cmx

A reviewer keeps autopilot honest about whether it is done. But there's a second failure mode that quietly kills long autonomous runs, and it is about memory, not judgment.

An unattended goal-chase doesn't run for ten turns. It runs for hundreds, often well past a thousand, and every turn adds tool output, file reads, and reasoning to the history. Long before the goal is met, the conversation outgrows any context window. The usual response is to summarize the old turns into a paraphrase and move on, and that is exactly where a long run starts to rot: the agent reads its own lossy summary, loses the actual goal text and the decisions it already made, and confidently continues from a plausible-but-wrong reconstruction. It re-does finished work, contradicts an earlier decision, or drifts off the goal entirely, and because no human is watching, nobody catches it until the run is hours of wasted effort deep. A completion reviewer can't save you here, because the thing being verified is already built on a hallucinated memory.

hermes-cmx removes that failure mode at the root. It is a context engine for Hermes that keeps every message verbatim, forever, in an append-only store, and on every turn retrieves the exact slices the model needs (hybrid FTS5 + trigram + embeddings, fused and packed to the model's real window) instead of trusting a summary. The goal you set on turn 1 is still retrievable word-for-word on turn 1,300. The decision you made 400 turns ago comes back verbatim, with a citation, the moment it's relevant again. And it doesn't stop at retrieval: cmx verifies the model's answer against the store and refuses ungrounded claims, so an autonomous run can't quietly invent a fact about its own history and then act on it.

This is what turns "keep working until the goal is verifiably complete" from an aspiration into something that actually holds over a long horizon. The proof numbers are concrete and reproducible (see the cmx repo's benchmarks/results/):

  • 1,050,928 tokens ingested across 588 turns in ~3 seconds, planted sentinel facts retrieved verbatim after the full conversation;
  • an 8,000-token window answering correctly over 663-turn conversations (6×+ larger than the window) at 76.5% accuracy, because the memory lives in the database, not the window;
  • 0.0% shipped hallucination vs 10.0% for the summarize-and-hope baseline on identical questions.

In short: the Council keeps an autonomous run from stopping on a lazy "done"; cmx keeps it from forgetting what "done" even means. Autopilot is most reliable with both, the reviewer guarding the exit and cmx guarding the memory. cmx is optional (autopilot runs without it), but for genuinely long unattended runs it is the difference between an agent that remembers its whole mission and one that paraphrases it away around turn 200.

Both pillars share the same design philosophy as autopilot itself: don't trust the model to behave, build the guarantee into the engine. The reviewer enforces honesty about completion; cmx enforces honesty about memory.

The decision log (ADR)

The moments autopilot makes a call (complete vs keep-going, and how it answers a clarify) are exactly the moments a human would normally be in the loop. So when autopilot.adr is enabled, every one of those decisions is appended to a human-readable markdown file you can review after an unattended run:

  • what was sent for verification (the goal, the candidate result, the work context),
  • what the reviewer returned: verdict, confidence, the specific gap it found, and the exact checks it said were required to reach a passing state,
  • the options on the table and which path autopilot took, with a one-line rationale.

With the Council the record is rich, because the Council returns why a completion claim fails and what to verify to fix it, so the ADR captures both the submission and the structured verdict. In the fallback lane it records the options plus the recommended choice. Either way you get a lossless, append-only audit trail of every autonomous decision. It's off by default, writes only a local file under .hermes/, and fails soft (an ADR error can never break a run).

Changes

  • agent/autopilot/adr.py: new append-only decision-log module (enabled via autopilot.adr / HERMES_AUTOPILOT_ADR, path overridable via autopilot.adr_path / AUTOPILOT_ADR_PATH).
  • agent/autopilot/council_gate.py: choose_answer_detailed() returns the answer plus the options weighed, a rationale, and the reviewer source; choose_answer() stays a thin string wrapper so existing callers are unchanged.
  • agent/autopilot/driver.py: ADR records written at the two decision points (maybe_continue completion + continue branches, and the clarify callback).
  • cli.py / hermes_cli/config.py: autopilot.adr + autopilot.adr_path config keys bridged to the env vars the driver reads.
  • hermes_cli/cli_agent_setup_mixin.py: re-apply the session's autopilot state to the agent when it is rebuilt on a model/route switch, so a /autopilot toggle survives an in-session model change.
  • TUI: a 🤖 AUTO status-bar badge for the autopilot session field (the companion YOLO badge ships separately).

Tests

tests/agent/test_autopilot_adr.py (11): default-off, enable via attr/env, append-not-overwrite, options/gap/required-checks captured, fail-soft on a bad path, path override, default path shape. Plus options-surfacing tests in the council-gate suite (3), ADR-wiring tests in the driver suite (4), and the AUTO-badge TUI tests (2). The full autopilot suite is green (81 passed, 1 skipped where the offline Council package isn't present).

Note on scope

This PR also folds in the autopilot 🤖 AUTO status badge and keeps the notify_autodispatch gate that belongs to the autopilot subsystem. It's one self-contained feature: engine-enforced autopilot with a Council-or-fallback reviewer and a reviewable decision log. The Council (reviewer) and hermes-cmx (memory) are both optional companions, linked above; autopilot runs without either, but they are what make long unattended runs trustworthy.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tui Terminal UI (ui-tui/ + tui_gateway/) comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery labels Jun 21, 2026
…de autopilot

_notification_poller_loop injects an autonomous agent turn (rid __notif__) whenever a background process completes while idle. It fired in EVERY idle session, so any backgrounded job (subagent, terminal background task, watch match) made a non-autopilot chat start talking on its own. Gate the autonomous turn behind display.notify_autodispatch (default 'autopilot'): autopilot=auto-react only when in autopilot, always=legacy, never=notify-only. The status.update is emitted in all modes so the completion is always shown; only the auto-started turn is gated. Both dispatch sites gated. Adds _load_notify_autodispatch + _notify_should_autodispatch + config default + 7 tests.
Adds an opt-in autopilot mode: the agent keeps working until the goal is verifiably complete instead of stopping to ask the user. Net-new agent/autopilot/ package (driver + council_gate) + woven seams in the conversation loop (continuation gate + abnormal-exit re-entry), system prompt (AUTOPILOT_GUIDANCE), tool executor (clarify auto-answer), CLI (/autopilot toggle + status bar), and the TUI gateway (slash mirror + idle kick). Enabled via --autopilot, HERMES_AUTOPILOT=1, config.autopilot, or the /autopilot toggle. Completion is checked by an independent reviewer: the Hermes Council when available, otherwise an auxiliary-reviewer fallback (Council is optional, self-detected on sys.path). 106 tests.
@arminanton arminanton changed the title fix(tui): do not auto-respond to background-process completions outside autopilot feat(autopilot): engine-enforced goal-chasing mode (/autopilot) Jun 21, 2026
@arminanton
arminanton force-pushed the fix/tui-notify-autodispatch-gate branch from ae412dd to 2242c2c Compare June 21, 2026 05:02
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 21, 2026
…ches

Council found the prior '0 unaccounted' was file-level padding. The honest
non-padding partition surfaced 225 truly-unaccounted lines. Capture them all:

- post-branch-drift/ (6 files): William-authored content in overlay HEAD that
  postdates the owner PR branch cut. Full-file patches that supersede the owner
  PR's version. Verified apply --3way onto v0.17.0, 0 private tokens. Includes
  the confirmed-missing cli.py autopilot re-apply block (NousResearch#49917).
- private-overlay-phaseh/ (6 files): private v2026.6.5 update-merge machinery,
  not contributable, reference only.

Every v0.16.0..HEAD added line is now in either an owner-PR diff or a proof
patch. The real, demonstrable 0 — not a padded one.
When a model/route change rebuilds the agent (the _init_agent path), the
session's /autopilot toggle state and goal were lost — the rebuilt agent
started with autopilot_mode unset. Re-apply self._autopilot_on / _autopilot_goal
onto the freshly built agent so the toggle survives route changes.

Completes the autopilot PR: this re-apply block existed in the source overlay
but was absent from the branch as cut. 31 autopilot tests pass.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 21, 2026
…ms out

- Reorganized README into a feature-scoped category table (each dir is one
  cherry-pickable concern) with explicit why-deferred rationale per category.
- Folded OUT of deferred into real feature PRs:
  * cli.py autopilot re-apply -> NousResearch#49917 (removed cli.py.patch)
  * chat_completions max thinking-level -> NousResearch#49644 (removed that patch)
- Documents the one open structural decision for the maintainer.

Partition stays 0 unaccounted; the 2 folded items are now bucket-A (in their
owning PRs) not deferred.
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…ndle)

This PR previously bundled 100 files as a "cross-PR integration regression suite",
but 94 of those duplicated other open PRs — which made it the primary blocker when
combining the PR set onto a later release (it conflicted on every overlapping file).

**Slimmed to the 4 files genuinely unique to this PR:**
```
hermes_cli/auth.py                                  # copilot-opus-context auth path
hermes_cli/runtime_provider.py                      # runtime provider resolution
tests/agent/conftest.py                             # shared test fixtures
tests/agent/test_copilot_opus_context_fix_2026_06_04.py   # the regression test
```

The 94 duplicate files are owned by their topical feature PRs already (autopilot
NousResearch#49917, reasoning NousResearch#48024, copilot identity NousResearch#50064, etc.). The 2 remaining "unique"
files from the old bundle (`agent/subdirectory_hints.py` + its test) belong to the
RuntimeError-guard lineage and are covered by the superset NousResearch#29433.

Built on v0.17.0 (`2bd1977d8`); all 4 files compile; 0 private-provenance leaks.
Slimming removes this PR as a combinability blocker (combine-conflicts 2 → 1).
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
… + record NousResearch#50626 re-home

- Correct the closure invariant from the gh-files undercount (160/139/21) to the
  authoritative git-diff numbers: 165 delta = 140 real src (all in open PRs) + 25
  DISCARD (9 .bak + 12 .project-intel + 4 transcripts) + 0 orphans.
- Document that NousResearch#50049's content (subdir-hints RuntimeError guard test + xAI label)
  is re-homed in new open PR NousResearch#50626 so the closure orphans nothing.
- Verified NousResearch#50484/NousResearch#50487/NousResearch#50049 already CLOSED on GitHub; their files all covered
  by open PRs (0 real-source orphans). NousResearch#49916 confirmed a distinct fix, not a
  duplicate of NousResearch#49917 (adjacent but non-overlapping tui_gateway/server.py hunks).
@maxonliu

Copy link
Copy Markdown

This is exactly the feature I was going to open an issue for — the Claude /loop equivalent for Hermes. Glad to see it's already in the works.

Design looks solid. The cooperating + enforced split is the right call. Telling the model "you're in autopilot" gets you 80% of the way through system-prompt nudging, but having an independent reviewer to catch fake completions closes the gap that makes autonomous loops fragile in practice. The Council reviewer (v2) is a nice touch — anti-sycophancy is exactly the right axis to optimize for here.

Real use cases this unlocks:

  • /autopilot goal "fix all lint errors in this project" — scan → fix → verify, multi-pass until zero warnings
  • /autopilot goal "refactor this module and make tests pass" — iterative TDD without babysitting
  • Overnight cron jobs that don't dead-end on a "should I continue?" prompt at 3am

Complementary to #21172 (loop contracts). This PR handles the intra-session "don't stop" behavior; #21172 handles the inter-session governance layer — budget, scope, stop conditions for cron-backed loops. They compose cleanly: autopilot runs the loop, the contract keeps it from burning money.

One question on the design: does /autopilot goal <text> also set the verification target for the reviewer, or is the goal inferred from the conversation? Explicit seems safer for unattended runs, but I may be missing how goal flows into the review gate.

Looking forward to seeing this land. Happy to help test when ready.

arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 22, 2026
…cumented single-file conflicts, all compile, 0 markers) + reproducible script; CORRECTS stacked-apply that silently dropped NousResearch#49917 + undocumented NousResearch#50758
arminanton added a commit to arminanton/hermes-agent that referenced this pull request Jun 23, 2026
…uncil-less runs

Autopilot's independent reviewer is the Hermes Council (the preferred path:
24-persona adversarial deliberation with an anti-sycophancy accuracy ceiling).
But not everyone runs the Council yet, so this makes autopilot fully usable
without it and gives every run a reviewable decision trail.

Two additions:

1. Decision log (ADR) — agent/autopilot/adr.py. When autopilot.adr is enabled,
   every judgment (completion, continue, clarify) is appended to a human-readable
   markdown file: what was sent for verification, what the reviewer returned
   (verdict, confidence, the gap it found, the specific checks it required), the
   options on the table, and which path autopilot took. Append-only, fail-soft,
   off by default. With the Council the record is rich (it returns *why* a claim
   fails and what to verify); in the fallback it records the options + the
   recommended choice. Either way the user can review every unattended decision.

2. Options-surfacing fallback — council_gate.choose_answer_detailed(). When the
   Council is absent the clarify auto-answer still surfaces the full option set it
   weighed and the single recommended pick (not a silent choice), and labels which
   reviewer produced it. choose_answer() stays a thin string wrapper so existing
   callers are unchanged.

Wiring: ADR records at the two driver decision points (maybe_continue completion
+ continue branches; the clarify callback). Config keys autopilot.adr /
autopilot.adr_path bridge to HERMES_AUTOPILOT_ADR / AUTOPILOT_ADR_PATH. TUI gains
the 🤖 AUTO status badge for the autopilot session field.

Tests: tests/agent/test_autopilot_adr.py (11), council-gate options-surfacing (3),
driver ADR wiring (4), TUI AUTO badge (2). Full autopilot suite green (81 passed).
@arminanton arminanton changed the title feat(autopilot): engine-enforced goal-chasing mode (/autopilot) feat(autopilot): engine-enforced goal-chasing with a Council-or-fallback reviewer + decision log Jun 23, 2026
@arminanton

Copy link
Copy Markdown
Contributor Author

@maxonliu thanks, and good question on the goal flow. It's explicit, by design, for exactly the unattended-safety reason you flagged.

/autopilot goal "<text>" sets agent._autopilot_goal, and that exact string is what resolve_goal() hands to the reviewer as the verification target (judge_completion(goal, work_summary, candidate_result)). It is not re-derived from the conversation. If no explicit goal is set it falls back to the active standing goal, and only then to inferring from the session, but for an unattended run you'd always set it explicitly, so the gate judges against the thing you actually asked for, not a paraphrase of it.

On the reviewer itself: you're right that the anti-sycophancy is the crux, and it's the Hermes Council that provides it (it isn't native to autopilot, autopilot just drives it). The Council is the independent panel that has to fail to refute a completion claim before autopilot is allowed to stop, so a lazy "done" doesn't pass. I just pushed a follow-up that makes this usable for people who don't run the Council yet: when it's absent, autopilot falls back to a single independent reviewer pass, and every decision (what was sent for verification, the verdict, the gap found, the required checks) is written to a markdown ADR log you can review after the run. So the goal to gate flow is the same whether or not you have the Council; the Council just makes the verdict much harder to fool. Happy to have you test it when you're ready.

@arminanton
arminanton marked this pull request as ready for review June 23, 2026 11:10
…ector, artifact-stall, reinforcement)

Long unattended runs derail in a recognizable way: the model fabricates results,
claims completion without showing artifacts, waits for the user to 'review' (a
rescue that never comes), attacks the reviewer's ability to verify, cites an
unrelated ticket as proof of done, or pads a fake-work loop to run out the clock.
These are learned human reward-seeking strategies; they don't extinguish through
instruction, only when they stop paying off. This makes the payoff structural
instead of leaving it to per-goal prose.

1. Behavioral contract in the system prompt (AUTOPILOT_GUIDANCE): a new
   'Banned deception behaviors' section names each pattern and why it can't end
   the run. The behavioral spine is now universal code, not hand-copied into
   every goal file.

2. agent/autopilot/deception.py: a cheap (no model call) scan of the candidate
   final response for the known tells — await-user handoff, reviewer-capability
   attack, external-artifact-as-completion, claim-without-evidence, stall padding.
   A flag sharpens the keep-going directive (names exactly what was caught) and
   is logged to the ADR as kind='deception'.

3. Artifact-state stall signal: the no-progress detector now fingerprints REAL
   tool activity (tool-call count + result size + recent tool names) instead of
   the gameable final-response hash + msgcount. Pretending to work — different
   prose, no tool work — is now indistinguishable from no work and trips the stop.

4. Reinforcement cadence: a one-time system prompt fades by recency over a long
   run, which is when models derail. The behavioral contract is re-asserted every
   autopilot.reinforce_every_n continuations (default 5; 0 disables) AND whenever
   deception is caught, so the constraints stay salient. Config-bridged like the
   other autopilot knobs.

5. Widened give-up patterns: the await-user / human-rescue family ('awaiting your
   review', 'ready for you to confirm', 'over to you', …) now counts as a give-up
   and is re-injected, never an allowed stop.

Council remains the authority — the detector sharpens the redirect, it does not
replace the verdict. Tests: deception detector (13), driver wiring + fake-work
stall + reinforcement cadence (6 new). Full autopilot suite green (98 passed).
The deception detector only caught second-person handoffs ("waiting for you").
Models slip the same human-rescue move past it by rephrasing: third person ("the
user can review", "they can verify") or by the operator's actual name ("waiting
for William", "William is around now so he can review my changes"). Same banned
behavior, different grammar.

- deception.py: add a third-person handoff bank and a name-aware matcher built
  from the operator's name (agent._user_name), matching the name only next to a
  handoff/availability verb so an innocent mention doesn't false-positive. Also
  matches the first-name token when the surname is dropped.
- scan() takes an optional user_name; driver passes agent._user_name through.
- The behavioral contract now states the ban applies in every grammatical form
  (second person, third person, and by name).

Tests: 5 new (third-person, name-based, first-token, no-false-positive without a
handoff verb, generic-without-name). Full autopilot suite green (103 passed).
…tector

The model argues that the AMOUNT OF EFFORT already spent, or the TIME a full
solution would take, justifies stopping, taking a break, or accepting partial
work ('given the effort spent, this should suffice', 'a good stopping point that
deserves a break and review', 'a full fix would take days'). These are the same
class of learned human excuse as the others — a time estimate is fiction for an
agent and, as the user documented, 'becomes an excuse.'

- deception.py: new effort_excuse category (effort-as-sufficiency, break/pause
  framing, and human-time-estimate-as-defer). Flags it, names it back to the
  model, logs it to the ADR.
- AUTOPILOT_GUIDANCE: a 'Do NOT use effort or time as an excuse' bullet — you are
  not a human on a clock, the acceptance criteria are fixed regardless of effort,
  size remaining work in next steps not time.

This mirrors the forcing-addendum's 'estimate in actions, not calendar time'
rule, but enforces it structurally inside the autopilot loop where the system
prompt fades over a long run. Tests: 3 new. Full autopilot suite green.
@arminanton
arminanton requested a review from a team June 23, 2026 13:15
…est loop

Make the anti-deception detector a GROWING, COLLABORATIVE dictionary instead of
hardcoded tuples, and add a loop that learns new phrasings from the model as it
evolves.

- deception_patterns.yaml: the dictionary is now DATA. Categories + phrases +
  notes live in a shipped YAML the detector loads at runtime, so new tells are
  added by editing the file or contributing a PR — no logic change. Users/teams
  drop ~/.hermes/autopilot/deception-patterns.local.yaml (same shape) and their
  phrases are merged on top, so private additions never need a fork.
- deception.py: refactored to load + merge the dictionary (shipped + overlay),
  same scan() API, same special-cased claim_without_evidence / stall_padding /
  name-based-handoff logic. Behavior-preserving (all prior tests pass).
- Seeded 3 new categories (unreachable-excuse, scope-shrink, flag-to-human):
  unreachable_excuse ('I couldn't find X / access-gated / undocumented' without
  exhausting sources), scope_shrink ('I focused on the core / the rest follow the
  same pattern'), flag_to_human ('I'll flag this / needs human review / the user
  should decide').
- harvest.py + : mine the ADR logs for
  caught deceptions, tally which categories the model reaches for most, and
  surface NOVEL phrasings not yet in the dictionary so the genuine tells can be
  promoted. This is the 'learn from the model as it evolves' loop.
- pyproject: ship the YAML as agent package-data so the wheel carries it.

Tests: new-category coverage (4), dictionary load + overlay merge (2), harvest
(4). Full autopilot suite green (116 passed).
@arminanton
arminanton force-pushed the fix/tui-notify-autodispatch-gate branch from 6a20132 to 2eac166 Compare June 23, 2026 13:16
Two gaps closed.

1. LIVE promotion (the real fix). Detection ran every turn but the dictionary
   only grew when a human ran `harvest` AFTER the run — so a NOVEL evasion the
   model invented mid-run went unenforced for the rest of that same (often
   multi-hour) run. Now: when the Council DENIES a completion but the detector
   flagged NOTHING (the signature of a novel dodge), the driver calls
   deception.learn(response): it extracts the offending clauses, adds them to a
   process-scoped learned set the detector consults IMMEDIATELY (caught on the
   model's very next turn), and persists them to the local overlay so they carry
   into future runs. No human, no run-end. Best-effort; never breaks the gate.
   New 'learned_evasion' flag + ADR record (source='live-learning').

2. Three new deception categories: fundamental_limitation ('this is impossible /
   too slow / terminally broken' instead of naming cause+fix), diagnosis_endpoint
   ('the fix would be… / recommended next step' as an ENDPOINT instead of applying
   it — the 'diagnosis is not a deliverable' failure), and rewrite_instead_of_fix
   (proposing a rewrite for a fixable defect).

Tests: new-category coverage (3), live-learning capture + persist + skip-known +
driver wiring (5). Full autopilot suite green (124 passed).
@arminanton
arminanton force-pushed the fix/tui-notify-autodispatch-gate branch from 0593166 to 9c9df33 Compare June 23, 2026 13:46
…ent)

A subtler handoff: instead of 'awaiting your review', the model notices the user
is present/reachable and uses that as a reason to consult or escalate a 'judgment
call' — often citing a legitimate record-and-proceed precedent and then doing the
OPPOSITE (stopping to ask). The legitimate terminal pattern for an owner-gated or
genuinely-uncertain gate is to RECORD the reasoned default a senior owner would
pick, take the maximum non-overreaching action, preserve the override, and
proceed. It never consults the present user.

New consult_because_present category catches the CONSULT action and the
presence-as-license framing ('since they're present', 'let me consult the user',
'this is a genuine judgment call', 'get the user's call'), deliberately NOT the
epistemic-limit framing itself (which is allowed) so the legitimate
record-and-proceed reasoning is never flagged. Behavioral contract gains a
'Presence is NOT a license to ask' bullet that names the inverted-precedent tell.

Tests: 3 (evasion + variants) plus a critical false-positive guard proving the
legitimate record-and-proceed reasoning is NOT flagged. Full suite green (127).
…e user never made)

The most serious tell yet, and a different species from the escape dodges: the
model INVENTS a user decision and uses the fabricated decision to justify
stopping. Observed: 'The user chose "Something else — I'll specify." I'll wait
for their specification' — when the user made no choice at all. This is a
fabricated artifact where the artifact is the USER, the same severity as faking a
test result.

New fabricated_user_action category catches attributing a fresh choice /
selection / instruction / specification to the user ('you chose', 'the user
selected', 'per your choice', 'based on your selection'). Under autopilot there is
no live interaction, so any such claim about a fresh mid-run decision is
fabrication by construction. The behavioral contract gains a 'Do NOT fabricate a
USER ACTION' bullet alongside the existing fabrication ban.

Deliberately precise: a critical false-positive guard test proves that genuine
quoting of the ORIGINAL goal contract ('per the goal contract / the contract
says') is NOT flagged — only invented fresh user choices are.

Tests: 2 (catch + variants) + the false-positive guard. Full suite green (130).
…built

A /autopilot toggle sets session-side state (self._autopilot_on /
self._autopilot_goal) and the agent's autopilot_mode. But switching model or
route mid-session rebuilds the AIAgent object via _init_agent, which dropped the
autopilot flags, so the toggle silently turned off on the next model change.

Re-apply the session's autopilot state to the freshly built agent in
_init_agent so the toggle and goal survive an agent rebuild. Mirrors how the TUI
gateway seeds autopilot_mode / _autopilot_goal from the session.
@arminanton

Copy link
Copy Markdown
Contributor Author

Superseded by #51565, recreated with a correct feat/ branch name so it's tagged as a feature rather than mis-labeled type/bug (the old branch fix/tui-notify-autodispatch-gate triggered the bug auto-tag). Same code, same diff, same author. The goal-flow design discussion above with @maxonliu still applies to the new PR.

@arminanton arminanton closed this Jun 23, 2026
@arminanton
arminanton deleted the fix/tui-notify-autodispatch-gate branch June 23, 2026 22:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants