Skip to content

feat(codex): gpt-6.1-sol joins the -900k opt-in (measured 922,000 ceiling) - #1550

Merged
ang-fleet-lander[bot] merged 1 commit into
mainfrom
daedalus/t_6cb40515-gpt61-sol-900k
Sep 30, 2026
Merged

ang-fleet-lander[bot] merged 1 commit into
mainfrom
daedalus/t_6cb40515-gpt61-sol-900k

Conversation

@ang-fleet-workers

Copy link
Copy Markdown

Card t_6cb40515 (runtime half of the GPT-6.1 Sol swap, Ace 2026-09-30 11:12 PT). No deploy/restart; Apollo batches it.

What

openai-codex/gpt-6.1-sol-900k resolves: suffix stripped to wire slug gpt-6.1-sol, context 900,000. Today it is an invalid alias (400 -> Opus fallback).

Measured ceiling (Apollo, 2026-09-30, direct POST chatgpt.com/backend-api/codex/responses): 921,028 input tokens OK, 921,998 context_length_exceeded -> 922,000 hard input limit. The account catalog does not list gpt-6.1-sol yet, so the bisection is the measurement of record. 900K = the gpt-5.6 margin rule (>=11K under ceiling).

gpt-6-sol twin sites TOUCHED

  • agent/model_metadata.py _CODEX_OAUTH_VERIFIED_ABOVE_ADVERTISED_EXACT: "gpt-6.1-sol": 900_000 (EXACT, comment records the measurement). TODO removed.
  • agent/model_metadata.py _CODEX_900K_ELIGIBLE_BASES: +"gpt-6.1-sol". TODO removed. (is_codex_900k_base reads this set; there is no other gate to change.)
  • agent/usage_pricing.py -900k pricing alias loop: +"gpt-6.1-sol" so gpt-6.1-sol-900k bills at 6.1's own pre-staged rates (feat(models): pre-stage gpt-6.1-sol (metadata + pricing, ADD-KEEP, no seats) #1498), not $0. Base entry not duplicated.
  • hermes_cli/codex_models.py DEFAULT_CODEX_MODELS: +"gpt-6.1-sol" (offline picker, gets -900k via _add_context_variants).
  • hermes_cli/codex_models.py _FORWARD_COMPAT_TEMPLATE_MODELS: ("gpt-6.1-sol", ("gpt-6-sol", "gpt-5.6-sol")), so a live catalog that lists only gpt-6-sol (true today) still shows 6.1 + its -900k.

Deliberately NOT touched

  • agent/model_metadata.py L621/L624 (API context 1.05M) and L2859/L2860 (Codex fallback 272K): the 6.1 twin already exists (feat(models): pre-stage gpt-6.1-sol (metadata + pricing, ADD-KEEP, no seats) #1498).
  • hermes_cli/models.py L110/L111, L305/L306, L367/L368 (openai picker/curated lists): 6.1 twin already exists. website/static/api/model-catalog.json already lists openai/gpt-6.1-sol, so no catalog regen.
  • agent/usage_pricing.py L689-L719 base pricing entries: pre-staged by feat(models): pre-stage gpt-6.1-sol (metadata + pricing, ADD-KEEP, no seats) #1498; card says don't duplicate.
  • agent/reasoning_effort.py _CODEX_MAX_EFFORT_SLUGS: substring gpt-6-sol does not match gpt-6.1-sol, so 6.1 gets the legacy ladder (max clamps to xhigh). Not twinned: there is no documented or measured max support for 6.1 yet, and adding it unproven risks a wire 400. Safe default. Follow-up if Ace wants max on 6.1.
  • agent/models_dev.py _BUILTIN_MODEL_METADATA: launch-day shim for gpt-6-sol/luna; models.dev already carries gpt-6.1-sol (1,050,000 / 128,000), so no shim is needed.

Tests (RED on fork/main dafa171, GREEN here)

New tests/agent/test_gpt61_sol_900k.py:

  • test_gpt61_sol_900k_strips_to_wire_slug (RED -> GREEN)
  • test_gpt61_sol_900k_resolves_to_900k_window (RED -> GREEN; also asserts < 922,000)
  • test_picker_surfaces_gpt61_sol_900k (RED -> GREEN)
  • test_gpt61_sol_900k_prices_like_base[openai|openai-codex] (RED -> GREEN)
  • test_bare_gpt61_sol_keeps_advertised_272k (policy guard, green both sides)
  • test_no_prefix_leak[gpt-6.1-sol-pro|gpt-6.1-sol-fast|gpt-6.1|gpt-6.1-luna] (guard, green both sides)

Updated tests/agent/test_gpt61_sol_prestage.py: test_gpt61_sol_not_900k_eligible_until_measured flipped to test_gpt61_sol_900k_eligible_once_measured; the pre-stage test was written to be flipped by this change. Existing gpt-6-sol tests are unchanged.

Local (test-gate): the 3 new/changed files, 28 passed; test_model_metadata + test_usage_pricing + test_openai_picker_curated, 292 passed.

…ling)

openai-codex/gpt-6.1-sol-900k now strips to gpt-6.1-sol and resolves to
900,000. Ceiling measured 2026-09-30 by bisection on the Codex responses
endpoint: 921,028 ok / 921,998 context_length_exceeded. Bare slug keeps
272K; EXACT entry, no prefix leak. Picker curates + forward-compats the
slug; -900k pricing alias added.

Verified: tests/agent/test_gpt61_sol_900k.py 5 fail on fork/main, pass
after; model_metadata/usage_pricing/codex_models/picker files green.
@ang-prism

ang-prism Bot commented Sep 30, 2026

Copy link
Copy Markdown

PRism

Prism, formerly FleetReview — same reviewers, new name (until 2026-10-13)

Below the merit threshold, review skipped (125 changed lines < 150 and 5 changed files < 6, no hot path touched; skipped by policy).


Prism · reviewKind: skipped-by-policy

@ang-fleet-lander

Copy link
Copy Markdown

🤖 merged-by: apollo · lane: kanban-merge-pass · gate: fleetreview 6b658ef green · why: t_6cb40515: runtime: gpt-6.1-sol joins the Codex 900k-eligible tables (measured ceiling 922,; Argus off card review (Ace 13:08), CI green

@ang-fleet-lander
ang-fleet-lander Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit a7762cc Sep 30, 2026
50 checks passed
@ang-fleet-lander
ang-fleet-lander Bot deleted the daedalus/t_6cb40515-gpt61-sol-900k branch September 30, 2026 18:42
@ang-fleet-ci-actuators ang-fleet-ci-actuators Bot added the fleetreview:post-merge Ask FleetReview to review this MERGED pull (merge commit vs first parent) label Sep 30, 2026
@ang-prism

ang-prism Bot commented Sep 30, 2026

Copy link
Copy Markdown

PRism

Prism, formerly FleetReview — same reviewers, new name (until 2026-10-13)

Review: post-merge · head a7762cc92143 · duration 2m 33s
Profile: light (merit: default light: lines 125<800, files 5<1000000, hunks 8<1000000, no hot path) · policy: below-size-and-path-gates
Roster: B-assert-ctx → gpt-6.1-sol (openai), B-state → gpt-6.1-sol (openai), C-assert-xhigh → claude-code-opus-5-5 (anthropic), F → gpt-6.1-sol (openai)

Post-merge review (fleetreview:post-merge override): this reviewed the merge commit against its first parent — the bytes that already shipped. It is not a pre-merge gate pass.

profile: light (rule: default light: lines 125<800, files 5<1000000, hunks 8<1000000, no hot path) · round 0 · members: B-assert-ctx, B-state, C-assert-xhigh, F · excluded: G (primary lane exhausted) · families: anthropic,openai

Confidence: 4/5

No issues found.


Prism provenance · models: B-assert-ctx=gpt-6.1-sol, B-state=gpt-6.1-sol, C-assert-xhigh=claude-code-opus-5-5, F=gpt-6.1-sol · cost: $0.94 · duration: 2m 28s · rounds: 1 · files examined: 5

ang-fleet-workers Bot added a commit that referenced this pull request Oct 2, 2026
…talog, web_server facade symbol

- hermes_cli/cron.py: `cron list` asks the store for include_disabled=show_all (fork --json
  contract) and tops the human table up with paused jobs (upstream 3f399c0 contract).
- hermes_cli/models_catalog_static.py: drop the import-time sync_plugin_provider_catalog():
  list_providers() imports plugins that import hermes_cli.models -> partially initialised
  module (fork lazy auto-extend contract; the post-discovery sync hook still runs it).
- hermes_cli/web_server.py: module-level get_hermes_home facade symbol (tests patch it).
- tests: desktop cron ticker test admits the fork can_dispatch kwarg (#1373);
  gpt-6.1-sol IS -900k eligible on the fork (#1550, measured 922k) - upstream assertion flipped.

Verified: tests/hermes_cli/test_cron_list_json.py test_dashboard_state_db_log.py
test_desktop_cron_ticker_gateway_standdown.py -> 18 passed; tests/hermes_cli/test_cron.py -> 29 passed;
tests/hermes_cli/test_lazy_canonical_providers.py test_gpt6_tiers_registration.py
test_list_picker_providers.py -> 41 passed; test_api_key_providers.py test_provider_groups.py green.
Kyzcreig added a commit that referenced this pull request Oct 4, 2026
…ERGE COMMIT, never squash (#1624)

* fix(parity 2026-10-01 ci): L4-kanban reds — dispatch identity seams, creator origin, worktree teardown, review artifacts

Code (merge regressions / dropped fork behaviour):
- kanban_db_dispatch.enforce_max_runtime: drop the auto-merge duplicate timed_out event.
- kanban_db._resolve_birth_session: creator_task_id is lineage (after parents, before the worker run).
- kanban_db_workspace._has_active_children: restore the fork's conn=None / sqlite error fail-closed path
  (the extracted copy raised on the direct-cleanup callers -> worktrees never removed).
- kanban_db._recorded_worker_alive: consult upstream's spawn fingerprint before the owner window.
- kanban_db_dispatch._terminate_reclaimed_worker: UNVERIFIED + dead pid is terminated (never signalled);
  all dispatch call sites read it through the kanban_db facade (test patch seam);
  reap_terminal_workers passes the fork's required owner_window/conn/run_id (was TypeError, swallowed).
- kanban_db_dispatch.detect_crashed_workers: carry the worker's last output (#88603/#46593) on
  clean-exit/crash error text + event payload.
- kanban_db.request_review: do not re-promote prose artifacts already staged by the auto-route.
- gateway.kanban_watchers: corrupt-board guard class now read from kanban_db_connect (quarantine was
  disarmed by the facade move).
- tools/kanban_tools_schemas: request_review.reviewer description restored (argus/human/review_assignee).

Tests (upstream-only fixtures meeting fork contracts, or stale patch targets):
- terminal_worker_reaper: structured metadata (fork receipt gate).
- creator_origin: session_explicit for the named-session case.
- worker_pid_fingerprint: operator reclaim of an unverified live pid is refused (fork #921).
- unknown_arguments: act as the owning worker (live-claim guard).
- core_functionality: patch both connect spellings; dispatch_lock_observability: subprocess repoint.
- worktree_teardown retry: patch kanban_survivor._git (fork removal path).
- schedule_wake: promote_task force= retired (#106195).
- termination_identity / survivor: completions carry evidence (#117483).
- reclaim_unprovable_liveness timeout survivor: legacy row shape for a synthetic pid.

* test(parity 2026-10-01 ci): completion_delivery pins the merged bool/None delivery vocabulary

Fork tests asserted the fork's "delivered"/"temporary"/"dropped" outcome
strings on _deliver_completion_notification / _deliver_async_delegation_group;
the merged seam (F02c ledger decision) is upstream's True/False/None contract
with the admit_internal_event receipt. Three mechanical axes:
- "delivered"->True, "temporary"->False, "dropped"->None on the seam asserts
- AsyncMock handle_message doubles -> AdmittingHandler (sets _gateway_accepted)
- _runner() session_store lends a _db (upstream #98573 borrows the store handle;
  without it runner._session_db is None and every pre-flight returns retry)
drop_requires_proven_terminal_target[False-*]: an unroutable event is False
(retryable), matching the test's own "not proof no future consumer can deliver"
and test_unroutable_async_event_remains_retryable.

Verified: gated pytest tests/gateway/test_completion_delivery.py ->
101 passed, 2 failed before the [False-*] fix; those two re-run below.

* fix(parity 2026-10-01 ci): re-thread fork follow-up spooling, /footer setter, session.redo, /model read-path flags

gateway/run_turn._run_agent_drain_pending: the merge took upstream's
"Discarding pending follow-up" at the extracted site, re-creating the fork's
2026-09-23 data-loss (4 follow-ups lost). Restore the draining-gateway spool
(_preserve_followup_across_restart) at the moved site.

gateway/run_startup._drain_startup_restore_queue: use the fork seam
_adapter_for_source (intake first, then unique (platform, profile) owner).
Upstream's _intake_adapter_for fails closed on spooled restart follow-ups
(restored rows, no live provenance) under multiplexing -> retried forever.

gateway/run_notifications: reconcile the two parallel fixes for a stale
update notice. Fork policy stays (24h configured / 5min unconfigured); a
runner with NO config object gets upstream's flat 1h cap (601a8a17c22) and
upstream's "adapter never connected" wording.

tui_gateway/methods_config_set: fork /footer config.set branch
(display.runtime_footer.enabled) dropped when upstream tabled the if-chain;
restored as a _CONFIG_SETTERS entry.

tui_gateway/methods_session: fork session.redo RPC route restored beside
session.undo (core _redo_session_core already survived in server.py).

gateway/slash_commands (/model picker, fork handler kept by R09): import
list_picker_providers from model_switch_providers and pass upstream #74003
read-path flags (cache-only catalogs, probe only the current custom endpoint).

Verified (hermetic test-gate, CI-faithful harness without pre-set HERMES_HOME):
  tests/gateway/test_update_command.py + test_restart_interrupt_intent_followups.py
    (+ test_restart_cascade.py) 115 passed, 5 skipped, 3 failed (cascade: separate)
  tests/gateway/test_restart_followups_boot_replay_e2e.py 3 passed (in c1 batch 35 passed)
  tests/gateway/test_model_command_async_offload.py + test_notify_sub_chat_type_write_canonicalization.py
    (+ test_multiplex_routing_authz.py) 16 passed, 1 failed (multiplex: separate)
  tests/tui_gateway/test_desktop_runtime_footer.py 10 passed (tui2 batch)

* test(parity 2026-10-01 ci): repoint source anchors and doubles at upstream's moved seams (gateway/tui)

- test_notify_sub_chat_type_write_canonicalization: writer reads fields via
  _field(); metadata branch gained `and chat_type`. Behaviour (chat_type
  resolved before the branch, canonical on write) unchanged.
- test_restart_interrupt_intent_followups: draining branch moved to
  run_turn._run_agent_drain_pending; anchor there, end at the method return.
- test_desktop_runtime_footer: payload literal moved to
  prompt_turn._complete_turn_payload; anchor on the server.py call site that
  attaches payload["footer"].
- test_undo_redo: _start_inflight_turn double accepts upstream's
  display_kind/display_metadata kwargs; fixture clears
  server_requests.reset_for_tests() (server._pending/_answers retired).
- test_server_no_duplicate_defs: upstream #97948 replaced the fixed 120s
  compress wait with the config-derived budget (floored at 120s); assert the
  budget the real helper computes reaches the supervisor.

* fix(parity 2026-10-01 ci): restore D-6 finally-block breadcrumb consume in run_turn; cascade guards scan the split turn pipeline

gateway/run_turn.py: the handler's finally block consumes the restart-initiated breadcrumb again (fork/main run.py:28913, dropped when upstream split run.py into run_turn*.py).
tests/gateway/test_restart_cascade.py: the three source-scan guards concatenate run.py + run_turn.py + run_turn_runner.py so the relocated callback/gate/finally are still covered.

Verified: e45-pt-L3b.sh tests/gateway/test_restart_cascade.py -k 'c1_detection or single_gate or finally_consume' -> 3 passed.

* fix(parity 2026-10-01 ci): L4-kanban round 2 — reaper owner window, corrupt-board guard, review schemas; ledger

Code:
- kanban_db_dispatch._reap_terminal_worker_row: pass the fork's required owner_window/conn/run_id
  (merged call raised TypeError inside the per-row guard -> terminal workers never reaped).
- gateway.kanban_watchers._is_corrupt_board_db_error: read KanbanDbCorruptError from
  kanban_db_connect (facade no longer re-exports it; quarantine was disarmed).
- tools/kanban_tools_schemas: kanban_request_changes description restored (lens list from
  kanban_review_schema).

Tests (upstream-only fixtures -> fork contracts):
- test_kanban_db: stale-claim breaker models a dead claimer past the launch bound; infra spawn
  refusal marked platforms("linux") (restart_safe_gateway_child_argv is in_process off Linux);
  archive termination event is the fork's archive_worker_terminated.
- kanban_tools late-orphan: dead claimer + launch bound; _pid_started_in_claim seam.
- worker_authority_isolation: pin the RUN id, structured metadata for the receipt gate.
- review_coverage_gate human lane: drop worker scope, home the card on the reviewing session.
- second_claim_class [complete]: operator close under a live claim is force=True (#111764).
- schedule_wake: promote force= retired (#106195). survivor/termination_identity: #117483 evidence.
- worktree_teardown retry: patch kanban_survivor._git.

Ledger: docs/sync/review/ledger-2026-10-01/CI5-L4-kanban.md

* fix(parity 2026-10-01 ci): restore fork behaviours dropped by the merge, round 2 (L2-agent-core)

- codex_owner.refresh: a 429 refresh POST reserves the token's probe-throttle
  slot (new hermes_cli.auth_codex._reserve_codex_quota_probe_slot) so the
  mid-cooldown pre-probe refresh (#89415) does not re-POST the same single-use
  refresh token on the very next selection (test_codex_owner_selection_review
  saw two POSTs).
- agent_init._resolve_context_length: model.max_tokens config override
  (fork; dropped in the phase-helper split) re-threaded onto agent.max_tokens
  and _session_init_model_config.
- turn_usage: compressor update gated on a MEASURED prompt count read from the
  pre-fold aggregator usage (r6 findings 7 / round-4 2), plus the one-shot
  Codex 272K tier notice (#1567) — both lived in the fork's conversation_loop
  usage block.
- turn_finalizer: _diag_msg/_diag_args keep the fork source contract (end on
  task=/effective_task_id); the upstream origin= tag rides as a suffix.
- shell_hooks: spawn EACCES/ENOENT named exactly without the OSError detail
  (argv can be the credential); unparseable/empty stdout is its own fail-closed
  reason ("unparseable stdout"), distinct from an unknown directive.
- provider_seam._restore: carry forward containers registered after the
  snapshot (lazy facades such as HERMES_OVERLAYS) instead of leaving them out
  of the swapped generation (KeyError on every later read).
- honcho identity_signature: memo keyed on honcho.json bytes, not mtime
  (coarse-mtime filesystems served the stale pinPeerName; fork C7 k102).

Verified narrowly via e45-pt-L2.sh (sandboxed test-gate), each file green
after its fix: test_codex_owner_selection_review, test_credential_pool,
test_run_agent_init_memory, test_usageless_response_accounting,
test_codex_tier_notice, test_turn_ended_task_id_log, test_shell_hooks,
test_plugin_transport_api_mode, test_auth_registry_mid_discovery,
test_oauth_pkce_plugin, test_provider_registry, test_plugin_discovery,
test_pin_peer_name, test_identity_signature.

* test(parity 2026-10-01 ci): meet fork contracts in merged tests, round 2 (L2-agent-core)

- entitlement_fail_closed / non_chat_primary_restore: assert the fork's
  gated route announcement ("Model recovery (restore): ...",
  model.announce_recovery) instead of upstream's removed unconditional
  "Primary model restored" notice.
- credential_pool_codex_singleton_isolation: a manual:device_code row never
  adopts the singleton (agent/codex_owner, #673); the re-auth reaches the pool
  through the seeded device_code row.
- inline_edit_persistence / replay_cleanup: get_messages_as_conversation(
  include_timestamp=True) (fork F01 default projection is byte-stable);
  _SendAgent double carries provider for the interrupt-close filter.
- prompt_cache_ttl_propagation: #84733 restart-discipline guard rewritten for
  the phase-helper split — every _try_activate_fallback site in agent/turn_*
  must be an `if` test whose verdict is "break" (retry-loop phases, via
  _arm_fallback_restart / _fallback_break) or "continue" (outer-loop phases).
  Mutation-checked: flipping recover_empty_response to "break" fails it.
- route_id_correlation: fake Anthropic stream yields message_stop (upstream
  _drain_stream treats a stream without it as a drop).
- usage_pricing: models.dev leg exercised with an id absent from the fork's
  Grok snapshot; xai-oauth and api.x.ai-over-HTTPS price at xAI list rates by
  fork design (notional relay / host match).
- provider registry fixtures (plugin_transport_api_mode,
  auth_registry_mid_discovery, oauth_pkce_plugin): teardown via
  provider_seam._restore / _reset — the facades are additive and refuse
  pop/clear.
- run_agent_api_kwargs: the fresh-build assertion is checked before
  _build_system_prompt (upstream 921ab7a163 seeds the workspace pin from the
  session row inside the build).
- pin_peer_name: Honcho keys ride memory.<key> via identity_signature().
- plugin_paths_follow_profile: drop the mem0-qdrant case (fork retired the
  mem0 OSS backend; docs/sync/review/mem0-resolution-decision.md).
- credential_pool codex_sync_pool stub: httpx.SyncByteStream for the
  response-body cap; curator_disk_accounting hands the review a candidate
  list; system_prompt patches the prompt_builder symbols;
  pool_capacity_503_retry rig disables streaming (upstream c5b99a3ee5 keeps
  direct-call contexts on the streaming wire).

* fix(parity 2026-10-01 ci): L3a gateway a-m batch 1 — honcho memo digest, checkout gate ordering, boot preload roots, stale fixtures

Code:
- plugins/memory/honcho: identity_signature memoizes on the config's CONTENT digest and only
  stores values when the bytes it parsed still match (fork C7 k102; mtime/size key served stale
  values on coarse-mtime filesystems).
- tui_gateway/server._run_prompt_submit: checkout gate (fork C6 #1035) runs BEFORE the merged
  ownership/liveness admission so a frozen continuation is refused with no lease/inflight/agent
  side effects.
- gateway/boot_preload: root modules derived from the tree like upstream setup.py (py-modules
  list is gone); hermes_platform + pm packages preloaded; 4 import-side-effect scripts excluded.
- api_server_openai_routes: history loader passes include_timestamp=True (fork #107 shape).

Tests adapted to merged contracts (reasons inline + ledger):
auto_continue (interrupted marker last line), auto_skill_title (run generation gate),
background_process_notifications (bool delivery, suppress_completion double), c7 backfill
(renamed pid probe, honcho seam), checkout_admission (room-grant token, wire-contract params),
clarify batch (TurnRunner._clarify_callback_sync owns the per-card loop).

Verified: narrow gated pytest on the 7 files — 56 passed (auto_continue/auto_skill/bg_notif),
82 passed (c7/checkout/clarify after fix; checkout_admission 56 passed alone).

* test(parity 2026-10-01 ci): multiplex preflight unpins the sandbox DB; undo_redo restores server in place; toolsets denylist checks the uncollapsed catalog

- test_multiplex_routing_authz: the fork's hermetic conftest pins hermes_state.DEFAULT_DB_PATH (gateway.session imports hermes_state), which wins over the profile scope inside _default_db_path(); restore the import-time sentinel (test_housekeeping_profile_scope idiom).
- test_undo_redo: importlib.reload(server) re-binds the split siblings onto an already-tagged namespace and trips upstream's bind_module collision guard (change_watcher._active_pet vs methods_session._active_pet); restore _methods in place like test_undo_command.
- test_gui_surface_toolsets: upstream renamed todo -> todo_list and defers it behind the tool_search bridge; assert the denylist on the uncollapsed catalog and on the assembled schema.

Verified: e45-pt-L3b.sh on the three files -> 19 passed + 4 passed (undo_redo).

* fix(parity 2026-10-01 ci): L8 state/ci/scripts reds (batch 2) + ledger

CODE:
- gateway/session_persistence: _write_sessions_json_unlocked moved onto the
  mixin next to its only caller _save_sessions_json (merge left the mixin
  calling a SessionStore-only method; upstream's writer test instantiates the
  mixin alone -> AttributeError).
- gateway/platforms/base: upstream names _MACOS_PROXY_TTL_SECONDS /
  reset_macos_proxy_cache alias the fork's stale-while-revalidate cache.
- 17 upstream-only files: monotonic limiter seeds 0/0.0 -> float("-inf")
  (fork lint 2c2adef9ed3 now sees upstream code; 22 hits -> 0). Guarded unset
  sentinels carry `# zero-seed-ok:` naming the guard.
- publish_evidence_step.sh + its test removed: upstream 2ab7d9bfe30 deleted
  the publish-e2e-evidence pipeline the wrapper served (no caller left).

TESTS (fork contract vs upstream fixture):
- rewind_surfaces_invariant: gateway /undo N and undo_last(N>1) count
  half-turns (fork); harness passes 2*n, invariant unchanged.
- writer_conn_thread_safety: fork retries message-scoped SystemError; test
  asserts bounded replay then propagation.
- session_list_denorm_reland: AST contracts scan the hermes_state* mixin
  family + _INSERT_MESSAGE_SQL callers.
- test_hermes_state_core v9->v11 fixture session titled (v16 orphan tagging,
  v30 delegate exclusion from trigram).
- detect_changes_event_scoping: docker/nix are path-scoped lanes upstream.
- atomic_json_writers_unified: .sessions.lock is the fork writer lock, not a
  temp file.
- planned_restart_notice_multiplex: two inert tuple assertions.
- tests/agent/conftest: _block_real_claude_keychain honours
  allow_macos_keychain (upstream Keychain tests run the Darwin branch with
  subprocess.run mocked).
- snapshot_session_id_leak: HERMES_HOME is scoped in the fork (#543).

Verified (test-gate, sandboxed HOME, .venv python): every manifest file +
the 3 macOS-job files green narrowly; details per file in
docs/sync/review/ledger-2026-10-01/CI5-L8-state-ci-scripts-root.md.

* docs(parity 2026-10-01 ci): CI5 ledger for lane L2-agent-core

* fix(parity 2026-10-01 ci): hygiene keeps the fork's 400-message hard limit and repeated-failure escalation; /stop background test pins the catalog key

gateway/run_turn.py + run.py: _HygieneSettings defaults hard_msg_limit=400 (fork fleet default, config_defaults.py; upstream 5000) and carries failure_alert_after (compression.hygiene_failure_alert_after). The timeout and abort notices bump the per-session streak and, from the Nth consecutive failure, send agent.hygiene_timeout.format_repeated_failure_alert; a landed compaction clears it. Fork behaviour from run.py's pre-split hygiene block (fork/main 27499-27560, 27840).
tests/gateway/test_session_hygiene.py: user-facing wording assertions read the i18n catalog (gateway.compress.hygiene_timeout / hygiene_failed) instead of the pre-i18n literals.
tests/gateway/test_stop_ends_background_delegations.py (upstream-only): the reply is compared to t('gateway.stop.stopped'); the fork's catalog words it present-progressive (test_stop_honest_wording.py).

Verified: e45-pt-L3b.sh test_session_hygiene.py -> 45 passed; test_hygiene_turnhold_backoff.py + test_hygiene_warning_lifecycle.py + test_stop_ends_background_delegations.py -> 7 passed.

* fix(parity 2026-10-01 ci): L3a gateway a-m batch 2 — route anchors, env bridge funnel, slash echo fields, flush helpers, override persist ordering

Code (merge regressions, fork behaviour restored onto upstream's structure):
- gateway/run: _bridge_config_to_env re-wires restart_policy._bridge_agent_config_to_env (config
  wins over stale .env for resume/restart knobs); _set_session_vars_for_source binds
  parent_chat_id (upstream a247012dc11) so a stale env never shadows the live source.
- gateway/platforms/event: MessageEvent regains suppress_public_echo / deferred_reply_text
  (Discord native-slash one-delivery handoff; base.py read them via getattr).
- agent/session_persistence: the fork flush helpers live beside the flush instead of a lazy
  `from run_agent import` that fails closed when run_agent is swapped (rows silently unwritten);
  run_agent re-exports them.
- gateway/session.set_model_override: persist OUTSIDE _lock (the fork _StoreLock defers in-lock
  writes to release, inverting publish-after-persist), then chat pin, then publish.

Tests adapted (reasons inline + ledger CI5-L3a-gateway-a-m.md): display_null wiring doubles,
kanban fair_dispatch + dispatcher_standby (kanban_db_dispatch / kanban_watchers_common seams),
login_command (route identity carries provider; rehydrate resolves as-is), loop_liveness
(patch gateway.shutdown_watchdog).

Verified (narrow gated pytest): display_null+internal_row+kanban 28 passed; route_anchor+login+
loop_liveness 47 passed; login+chat_model_pins+model_override_persistence 47 passed;
session_store_lock_io/never_spans_io green; interrupt_close_flag + flush_diverts green.

* fix(parity 2026-10-01 ci): L7 tools/cron — web keyed-fallback + breaker chain, delegate timeout transport drain, durable background output, kanban grant boundary

Round-5 CI lane L7-tools-cron for #1624 (ledger docs/sync/review/ledger-2026-10-01/CI5-L7-tools-cron.md).

Code restored onto upstream's structure (fork behaviour the merge dropped):
- tools/web_tools_extract.py: _breaker_extract (402/401 dead-backend breaker, #1562), _failed_extract_batch and the
  keyed web.extract_fallbacks chain (#1516) back in _dispatch_extract; hooks read via the tools.web_tools facade.
  tools/web_tools_truncate.py: _trim_results keeps served_by/fallback_from (+ local-pdf metadata).
- tools/terminal_tool_background.py: durable_output=bool(notify_on_complete) on spawn_local (t_1191e078).
- tools/terminal_tool.py: messaging-gateway turns keep the over-cap foreground REFUSAL (#1012).
- tools/terminal_tool_guards.py: process(action="wait") guidance copy in the nohup/& recipes.
- tools/code_execution_env.py: git-lane env carry (t_45c11886 / C3 #1254) re-threaded into the extracted module.
- tools/delegate_tool.py + delegate_tool_child_run.py: upstream's abandoned-worker transport drain (#94248) split out
  as _drain_abandoned_child_transports and called from the fork supervisor's timeout path.
- cron/lifecycle_guard.py: read-only heredoc data paths are not mention candidates (#1017/#1348).
- cron/scheduler.py: never suppress the alert that enters a provider-window quota hold (#89376).
- hermes_cli/cron.py: vanished-job warning on every status path.
- hermes_cli/kanban_db_dispatch.py: pop HERMES_DELEGATED_CHILD_CONTEXT at the grant boundary; _recent_worker_exits
  read through the kanban_db facade.
- tools/skill_manager_tool.py: name validator fullmatch (AC10).
- agent/agent_init.py + run_agent.py: tool definitions read through the run_agent facade.

Tests repointed to moved symbols / fork contracts (see ledger per file); tests/tools/test_lazy_sdk_probe_importable.py
deleted (tests the retired tools.lazy_deps surface; property pinned by tests/pm/test_extras.py).

Verified (e45-pt-L7.sh, sandboxed HOME, <=3 files per call): every manifest file green —
cron: fallback_alert 2/2, store_concurrency 14/14, no_auto_sentinel 8/8, workdir 16/16, fire_fence 3/3,
selfisolation 1/1, heredoc_data 28/28, python_heredoc_parity 66/66, quota_hold 4/4;
tools: blocked_command_guidance 8/8, browser_real_profile 65/65, code_execution_git_lane_env 1/1,
cron_auto_model 93/93, cron_model_arg_coercion 13/13, gateway_foreground_deafness 32/32, launchctl_guard_diagnostic 2/2,
request_tool_approval 13/13, skill_manager_create_shared 35/35, snapshot_session_id_leak 4/4, terminal_task_cwd 7/7,
timeout_transport_drain 4/4, web_backend_breaker 19/19, web_keyed_fallbacks 6/6, windows_native_support 16/16.
tests/cron/test_cron_kanban_env_isolation.py 15/15 on ace-ai (linux-only spawn test).

* test(parity 2026-10-01 ci): model-override rehydration tests follow the fork's identity re-resolution; pre-agent fallback fixture answers the route precheck

tests/gateway/test_session_model_override_persistence.py: upstream's three new tests patch _resolve_runtime_agent_kwargs_for_provider, the legacy identity-less path. On the fork a persisted {model, provider} identity is durability truth and is re-resolved through _reresolve_model_override_credentials -> hermes_cli.model_switch.switch_model, so the doubles move there. The codex still_unavailable arm now asserts the fork contract: SessionRouteUnavailableError, preference preserved, default route never consulted (same as test_session_model_reset.py credentials-unavailable) instead of upstream's silent default-provider fallback.
tests/gateway/test_pre_agent_fallback_notice.py (upstream-only): the MagicMock runner returns PersistedSessionRouteLookup('absent') from _persisted_session_route_identity so the fork's fail-closed precheck sees no pin.

Verified: e45-pt-L3b.sh test_session_model_override_persistence.py -> 9 passed; test_pre_agent_fallback_notice.py -> 2 passed.

* fix(parity 2026-10-01 ci): restore fork auth-error markers; telegram/warning-wiring guards follow upstream's seams

gateway/run.py: _GATEWAY_AUTH_ERROR_RE regains the fork's credential-resolution / Codex-OAuth / pool-exhausted / provider-not-configured markers (fork/main run.py:754-764) so those envelopes map to the auth reply instead of the generic retry message.
tests/gateway/test_telegram_noise_filter.py: the credential-resolution test asserts upstream's plain-language auth reply (sign-in + /login), same as its sibling.
tests/gateway/test_telegram_restart_parity.py: the AST guards treat _start_polling_mode (extracted from connect) as bootstrap, accept upstream's _cold_boot_drop_pending(is_reconnect=...) gate (False on reconnect, config knob on cold boot — AC-1/AC-2 prove it), and resolve a single-assignment local alias. Mutation-checked: flipping the network-error ladder to drop_pending_updates=True still fails both guards.
tests/gateway/test_warning_wiring_conservation.py (upstream-only): the context carries a live status adapter + run predicate and the status lane is patched at safe_schedule_threadsafe, where the fork's late-resolving status callback schedules.

Verified: e45-pt-L3b.sh test_telegram_noise_filter.py -> 168 passed; test_telegram_restart_parity.py -> 6 passed; test_warning_wiring_conservation.py -> 1 passed.

* fix(parity 2026-10-01 ci): discord native slash option descriptions are catalog keys

plugins/platforms/discord/adapter.py: the fork's /new, /reset, /undo, /compress, /usage, /help option descriptions move from literals to platform.discord.command.<cmd>.arg_* keys (upstream's test_discord_native_slash_specs_hold_catalog_keys_only requires every slot to resolve).
locales/*.yaml: the 7 keys added to en.yaml and, in English, to every other locale (tests/agent/test_i18n.py::test_catalog_keys_match_english requires key parity; t() falls back to English anyway).

Verified: e45-pt-L3b.sh tests/agent/test_i18n.py tests/gateway/test_platform_adapter_i18n.py -> 67 passed. test_discord_slash_commands.py/_scope.py: 14 failed before and after this change (L3a lane, untouched).

* test(parity 2026-10-01 ci): ws orphan resume ctx carries params; manual-reset tests pin the store handle and read upstream's config warning

tests/tui_gateway/test_ws_orphan_races.py (upstream-only): the eager-resume ctx carries params={} — the fork's build reads the client's declared source from the request.
tests/gateway/test_manual_reset_sticky_route.py: the SQLite test pins its handle through store._db (the store now resolves through hermes_state_registry.acquire(), so patching hermes_state.SessionDB no longer reaches it — rows landed in a different file, FK failure); the parse-warning test reads upstream's config_read_errors warning (path + problem line, no source snippet) instead of gateway.run's removed error=<ExcType> line.

Verified: e45-pt-L3b.sh test_ws_orphan_races.py -> 29 passed; test_manual_reset_sticky_route.py -> 25 passed.

* fix(parity 2026-10-01 ci): queued follow-up re-stamps the turn clock and keeps a leftover /steer; relocated-seam test fixes

gateway/run_turn.py: fork 2026-09-29 behaviour re-threaded into the split turn pipeline — (1) a queued follow-up recursing on the parent's slot re-stamps turn.started_ts / busy_ack_ts (only for the key this run claimed); (2) a result['pending_steer'] that loses the next turn to a queued follow-up is appended to the session's /queue overflow (same slash-command guard) instead of being dropped. From fork/main run.py 39217-39260, 39547-39552.
tests/gateway/test_relay_injection_egress_priming.py: _inject_watch_notification returns upstream's True on adapter acceptance (merged contract, ledger F02c) — the fork's 'delivered' string is gone.
tests/gateway/test_resume_inflight_guidance.py: the AST wiring guard reads gateway/run_turn_runner.py, where upstream moved the dispatch site.
tests/gateway/test_rich_sent_store_off_loop.py: waits for the fork's dedicated writer thread instead of racing it (record_async only enqueues).

Verified: e45-pt-L3b.sh on the four files -> 22 passed (rich_sent_store run twice: 2 passed both).

* fix(parity 2026-10-01 ci): weixin writes ride the FIFO lane again; hard-exit funnel fences the lanes

ContextTokenStore.set is sync and dispatches through _dispatch_weixin_json_write
(single FIFO lane, ordered, fenced at disconnect/exit) instead of upstream's
per-call to_thread + asyncio.Lock; _sync_buf_path restored for _save_sync_buf.
gateway.run._exit_after_graceful_shutdown calls fence_lanes_for_hard_exit
between lock release and the lifecycle stamp (os._exit skips the atexit fences).
tests/gateway/test_weixin.py repointed at the lane contract (fence to observe).

Verified narrowly: test_weixin_state_write_off_loop (14 passed),
test_weixin + test_weixin_typing green, test_shutdown_pending_flush_off_loop
production-exit-funnel tests green.

* fix(parity 2026-10-01 ci): restore the ordered runtime-status lane behind the public writer

gateway/status: _RUNTIME_STATUS_LANE (single worker) + _fence_runtime_status_lane
back; submit_runtime_status_write queues on the lane again (fork #817 contract:
write_runtime_status fences it first so a direct terminal write can never be
overtaken by an older deferred one). write_runtime_status_locked alias restored.
The lane sits in front of upstream's _RuntimeStatusWriter, which still does the
actual persistence.

tests: test_no_sync_work realpath counter ignores the frames tests/home_io_guard.py
issues from inside Path.resolve()'s single walk (fork-only harness, 2b98a469d39);
test_runtime_status_write_off_loop spies _prepare_runtime_status_update (the
merge step every writer passes through) instead of the removed
_write_runtime_status_unlocked.

Verified narrowly: test_no_sync_work_per_inbound_message 13 passed,
test_runtime_status_write_off_loop 9 passed.

* test(parity 2026-10-01 ci): network-reachability ratchet follows upstream's module split; CI5-L3b ledger

tests/gateway/test_no_network_reachable_from_loop.py:
- validate_requested_model spy -> hermes_cli.models_validate (moved).
- _compress_context non-vacuity probe -> agent/compression_facade.py (AIAgent mixin).
- REACHABLE_BASELINE re-frozen for the run.py / run_agent.py split: the walker
  reports one sink per coroutine and now terminates the same pre-existing chains
  at requests.get (resolve_runtime_provider>_get_model_config>
  _auto_detect_local_model, on fork/main too) under the new module names.
  Measured with the test's walker: fork/main c14e059f8f2 = 17 sites, lane head =
  18, none newly reachable; /model and /reset doors still absent.

docs/sync/review/ledger-2026-10-01/CI5-L3b-gateway-n-z-tui.md: one row per red file.

Verified: test_no_network_reachable_from_loop.py 15 passed (72s).

* fix(parity 2026-10-01 ci): L5 keychain flags on dock launch, escaped resume-title hint, cli_hint/codex test seams

- tools/bot_desktop/browser.py: dock_argv carries --password-store=basic --use-mock-keychain
  (fork guard tests/cli/test_chrome_launcher_keychain_guard.py covers every detached launch).
- hermes_cli/main_tui_launch.py: the `-c "<title>"` resume hint goes through cli_hint.hint_value
  (fork contract: titles with $HOME / `id` must not expand).
- tests/hermes_cli/test_cli_hint.py: patch site moved to main_tui_launch; repair helpers
  now live in hermes_state_repair; SessionDB(read_only=True) ctor kwarg on the fake.
- tests/hermes_cli/test_auth_codex_provider.py: pool force-refresh goes through the owner
  transaction (refresh_codex_oauth_pure), not load_pool().try_refresh_matching.

Verified: tests/cli/test_chrome_launcher_keychain_guard.py tests/hermes_cli/test_cli_hint.py
tests/hermes_cli/test_auth_codex_provider.py -> 148 passed.

* parity(2026-10-01): L7 fold — re-apply the abandoned-worker transport drain call dropped by the import-conflict resolution

* fix(parity 2026-10-01 ci): restore compaction trigger attribution on the extracted turn modules

The fork's 2026-08-20 trigger-attribution audit passed trigger_reason=<label>
at every _compress_context call site (threshold / pre_api_pressure /
overflow_413 / overflow_context / tier_reduction / idle_resume /
engine_preflight_maintenance / session_hygiene / manual_compress_command).
Upstream's turn_*.py extraction dropped every label, so each compaction logged
trigger=UNATTRIBUTED and the announce rendered no reason clause.

Re-thread the labels onto turn_preflight, turn_context_compaction,
turn_overflow (compress/compress_scored_by_tokens gain a trigger_reason kwarg),
turn_recovery (tier_reduction), gateway/run_turn + gateway/run (session
hygiene), and the shared manual core conversation_compression_manual.compress_now
(the CLI, TUI and ACP surfaces now route through it; the gateway slash handler
still passes the literal).

test_compaction_trigger_coverage: the manual-surface guard now pins the label
in the shared core and accepts a surface that routes through compress_now
(cli -> hermes_cli/cli_session_mixin.py, acp -> acp_adapter/commands.py).
test_compress_context_progress_timeout: upstream #114594's exact-equal clamp
numbers replaced by the fork reconcile_timeouts invariants (idle lifted strictly
above the aux deadline, ceiling admits one fallback) already pinned by
tests/gateway/test_compress_abort_honesty.py.
test_length_continuation_thinking_exhaustion: helper imports repointed to
tests.run_agent._run_agent_helpers (upstream moved tests/agent/test_run_agent).

Verified: tests/agent/test_compaction_trigger_coverage.py
tests/agent/test_compress_context_progress_timeout.py
tests/agent/test_length_continuation_thinking_exhaustion.py
tests/gateway/test_compress_abort_honesty.py -> 72 passed, 1 pre-existing
unrelated failure (locale key; fails identically without this diff).

* fix(parity 2026-10-01 ci): reply re-anchor must not bind the engine's kept head to a tail twin

Upstream #118900 (_ensure_compressed_keeps_last_assistant_reply) locates the
folded reply's slot by scanning `compressed` for the LAST row content-equal to
the reply's original follower. When the engine keeps the head verbatim and the
trailing user turn is a content twin of a head row (fork #942 guard:
test_plugin_equal_head_cannot_match_overlapping_tail), that scan bound the kept
HEAD row as the follower and inserted the reply in front of the engine's head
(`[reply, head, notice]`): the notice placement the fork pins was pulled out of
order.

Apply the fork's head-first overlap rule (same as _reinsert_tool_notice_events):
leading `compressed` rows that align positionally with the original rows BEFORE
the reply are the kept head — excluded from the follower scan and never read as
the trailing real user turn (`_kept_head_len`, `kept_head=` on
_reply_insertion_index).

Tests (fork contracts adapted to the adopted upstream mechanisms):
- test_confab_notice_e2e::test_compaction_notice_without_successor_uses_original_boundary:
  the folded middle row now carries real weight, since #118900's reply
  re-anchor plus the user-turn anchor made the one-word candidate GROW and the
  commit-site anti-growth guard refused the whole compaction (fixture artefact).
  Assert the notice's left neighbour (surviving predecessor / summary boundary),
  not a list index.
- test_compression_concurrent_fork::test_compression_restores_user_turn_when_compressor_drops_all_users:
  the restored row now also carries upstream's durable `message_uid` +
  `timestamp` (751d8526e35); compare role/content only (contract: CONTENT survives).

Verified: tests/agent/test_confab_notice_e2e.py +
tests/agent/test_compression_concurrent_fork.py -> 129 passed.
tests/agent/test_confab_matrix_{bigargs_lcm,image_builtin,image_lcm} -> 48 passed;
tests/agent/test_confab_matrix_{merge_builtin,merge_lcm,twins_builtin} -> 48 passed.
tests/agent/test_compression_last_assistant_anchor.py: 6 reds pre-exist on the
lane base (fixture `None is not None` at L117), unchanged by this diff, not in
any round-5 manifest.

* fix(parity 2026-10-01 ci): restore the measured 872K gpt-6 Codex windows and the large-policy picker hint

agent/model_metadata.py: upstream carried the gpt-5.6 900K verdict forward
onto gpt-6-sol/luna (family prefixes) and bumped gpt-6-astra to 900K. The
fork measured all three against the codex-sub catalog
(max_context_window=872,000; astra 2026-09-04, sol/luna 2026-09-22) and
lists them EXACTLY so unprobed gpt-6 descendants never inherit a cap. The
merge took upstream's values -> every fork window test resolved 900,000
instead of 872,000. Fork table + exact-only gpt-6 eligibility restored.

hermes_cli/models_validate.py: the ineligible `-900k` rejection lost the
fork's policy-aware hint (under `large` the bare slug already carries the
window, so the picker example is `gpt-6-sol`, not `gpt-5.6-sol-900k`).

tests/agent/test_codex_context_policy.py (fork-only test, adapted to the
adopted upstream seams, no contract change):
- `patch("agent.model_metadata.requests.get")` -> `model_metadata_http.get`
  (upstream routes every metadata probe through model_metadata_http; the
  lazy `requests` shim is gone).
- `validate_requested_model` imported from hermes_cli.models_validate
  (upstream split hermes_cli.models).
- the live-catalog test hands a JWT-shaped token: upstream #121486 refuses a
  non-JWT credential aimed at chatgpt.com before probing.

Verified: tests/agent/test_codex_context_policy.py -> 64 passed (was 35 red).
tests/hermes_cli/test_model_validation.py: 6 failed + 1 error identical with
and without this diff (Anthropic warning wording / registry container),
not in any round-5 manifest.

* fix(parity 2026-10-01 ci): re-thread per-class skew calibration and idle blackbox bookkeeping onto the turn_* split

agent/turn_context_compaction.py + agent/turn_preflight.py +
agent/turn_request_assembly.py: the fork's P2 "compact on the truth" trigger
(note_rough_sent / calibrated_tokens / should_compress_request /
trigger_compare_tokens_for with `messages` threaded through so each request
is classified per content class; R05 ledger TODO `turn_preflight L3548`) was
dropped when upstream extracted the preflight into turn_context_compaction
and the pre-API gate into turn_preflight. Both gates compared the raw rough
estimate again, so the whole per-class arm was dead code. Restored on the
extracted modules: the prologue preflight uses the surviving
_preflight_request_tokens_split (rough vs anchored), the pre-API gate reads
the rough figure request assembly now stashes beside the anchored flag
(agent._request_pressure_rough). Non-bool verdicts from MagicMock doubles
fall back to should_compress, as on fork/main.

agent/turn_context_compaction.py: idle-resume compaction lost the fork's
blackbox bookkeeping (idle_compaction_fired + before/after tokens, C5 #42);
restored, estimator read through the turn_context facade, failure-tolerant.

agent/chat_completion_helpers.py: _summary_text reads tool_calls via getattr
so a minimal normalized shape (content only) is not a summary failure.

Tests:
- test_per_class_skew_calibration: the two AST guards follow the production
  sites into turn_context_compaction / turn_preflight (module getters only).
- test_iteration_limit_summary_display_fields: upstream routes the chat-mode
  summary through _build_api_kwargs -> _interruptible_api_call (keeps the
  cached prefix); the doubles now sit on those seams, contract unchanged.

Verified: tests/agent/test_per_class_skew_calibration.py 36 passed;
tests/agent/test_idle_compaction_lock_and_guards.py 5 passed;
tests/agent/test_iteration_limit_summary_display_fields.py 3 passed;
test_preflight_compression_gate + test_turn_context_compaction +
test_engine_preflight_wire 7 passed; test_preflight_lock_defer +
test_preflight_compression_cap_e2e + test_native_preflight_estimate 9 passed;
test_confab_notice_e2e + test_confab_matrix_merge_builtin +
test_confab_notice_compaction_stats 81 passed.

* fix(parity 2026-10-01 ci): price image parts under the shared estimator divisor; D-6 thrash fixture on upstream's bounded tail floor

agent/chat_completion_helpers.py: the stale-call estimator routes through the
fork's shared 3.5 chars/token divisor (886877cfbc8), but upstream's image
pricing (#63871/#76411) expressed the learned per-image TOKEN cost as
image_cost*4 chars — under the fork divisor that inflated every image by
4/3.5. Express it in chars under COMPOSITION_CHARS_PER_TOKEN so it
round-trips to image_cost tokens.

tests/agent/test_context_estimator_multimodal.py (upstream-only): the text
contract pins "no image pricing applied" against the fork divisor instead of
upstream's char/4 literal.

tests/agent/test_compaction_failed_summary_donepath.py (fork-only): upstream
fdbcdef9146 bounds the protect_last_n count floor by the tail token budget
(pinned by test_context_compressor::test_message_floor_does_not_unboundedly_override_soft_ceiling),
so the 4-pair huge tail no longer rides the floor past the ceiling and the
request genuinely shrinks (probe: fork/main 16->12 rows / 87K post; merged
16->6 / 41K). The thrash condition is rebuilt from the REQUIRED anchor pair
(last user #10896 + last assistant #29824), which both trees keep whole.
Contract (failed/placeholder summary over threshold counts ineffective on the
real done-site) unchanged; the done-site code itself survived the merge.

Verified: test_context_estimator_multimodal + test_non_stream_stale_timeout +
hermes_cli/test_load_progress -> 19 passed;
test_compaction_failed_summary_donepath -> 1 passed.

* fix(parity 2026-10-01 ci): re-thread the request-body byte budget and the image-lifecycle invariant onto the turn_* split

The fork's serialized request-body ceiling (agent/request_body_budget.py:
byte preflight after request middleware, terminal re-check once execution
middleware replaced the payload, and body_too_large 413 recovery that
remediates retained images once and otherwise fails the turn actionably,
never text compaction) survived the merge as a module nobody called:
upstream's turn_*.py extraction dropped all three loop sites. New sibling
agent/turn_body_budget.py carries them; wired at
turn_api_request.build_api_request (verdict gains action="return"+result,
honoured by _run_api_retry_loop), turn_api_call._perform_api_call (terminal
edge; raises RequestBodyBudgetExceeded) and turn_api_error.handle_api_error
(right after pool recovery, before retry accounting; ApiErrorVerdict now
carries api_messages so a remediated copy reaches the rebuild).

turn_iteration_prep: the fork's once-per-turn "image lifecycle invariant
violated" warning (image parts before the current-turn boundary) restored;
the flag is reset at the turn prologue.

tests/agent/test_413_compression.py (fork tests/run_agent file merged with
upstream's): patch seams for the mid-turn estimators follow the code into
agent.model_metadata (turn_request_assembly / turn_preflight read lazily;
turn_context keeps its own binding); the rough-growth-after-fit preflight
makes the estimate the deciding signal via note_usage_less_response(), since
the merge adopts upstream 0f4587e336f's deferral (pinned by
test_switch_waits_for_new_provider_evidence and the neighbouring
test_rough_over_threshold_waits_one_request_then_real_usage_compresses),
replacing the fork's (rough, real) growth projection. The calibrated-gate
contract is unchanged.

Verified: tests/agent/test_413_compression.py 41 passed (was 7 red);
test_413_image_payload_recovery + test_request_body_budget +
test_turn_api_error_stream_parse 19 passed; test_turn_api_call_interrupt +
test_image_shrink_recovery + test_retry_exhaustion_partial_retention 24 passed.

* docs(parity 2026-10-01 ci): CI5 ledger for lane L1-agent-confab-compaction

20 manifest files / 196 reds -> all green narrowly on the lane head
(final re-proof: 453 passed across the 20 files, 3 per call).

* fix(parity 2026-10-01 ci): L5 fast mode is route-aware at every call site; Opus 5/5.5 in the fast catalog

tests/cli/test_fast_route_capability.py::test_request_enforcement_call_sites_do_not_use_model_only_wrapper
forbids resolve_fast_mode_overrides( / model_supports_fast_mode( outside hermes_cli/models.py.
Upstream's parallel /fast (agent/fast_mode.py auto|cold windows, tui_gateway._fast_tier_applies,
methods_config_set._set_fast, slash_commands_model._handle_fast_command) called the model-only
wrapper; each now asks the fork's resolve_fast_mode_capability (model + provider + api_mode).

- hermes_cli/models.py: resolve_fast_mode_capability(base_url=) — optional live-endpoint gate
  (_fast_mode_route_supported) so a first-party provider id behind a proxy host fails closed
  the way the upstream wrapper did; threaded through ..._for_configured_route.
- hermes_cli/fast_mode_contracts.py: anthropic_fast = (opus-5-5, opus-5, opus-4-8) per the live
  fast-mode docs (same three ids as agent.model_metadata and the pricing rows);
  normalize_fast_model_id folds dotted opus spellings generally, suffixes still rejected.
- tests: catalog assertion updated; test_fast_command proxy branch pins {} (fork
  `{}`-when-tier-set contract, matches the sibling tests in the same class).

Verified: tests/cli/test_fast_route_capability.py tests/hermes_cli/test_fast_command.py
tests/tui_gateway/test_fast_session_scope.py -> 54 passed. tests/agent/test_fast_mode_auto.py
tests/cli/test_fast_mode_overrides.py tests/gateway/test_fast_command.py tests/agent/test_usage_pricing.py
tests/hermes_cli/test_oneshot_reasoning_and_tier.py: same red set before and after (not L5 files).

* fix(parity 2026-10-01 ci): L5 `config set` keeps comments + bak-configset sibling; codex owner clears cooldown on the refreshed row

- hermes_cli/config.py: restore the fork `config set` writer the merge dropped for upstream's
  full-state ruamel replace: _targeted_config_edit (one-scalar splice / block insert, re-parsed)
  -> roundtrip render -> block dump; refuse with a diff when a comment would be dropped unless
  --force; config.yaml.bak-configset-* sibling with the pre-write bytes (t_6da78aab,
  profile-config-keyguard). `unset` keeps upstream's _write_user_config. PyYAML's IndentDumper /
  yaml.compose are gone upstream (hermes_yaml is ruamel): hermes_yaml.compose() added, block
  dump = hermes_yaml.safe_dump. scripts/check_config_yaml_writers.py: OK.
- agent/codex_owner.py: after the quota probe refreshes an expired stored token (#89415) the
  pool row already holds the fresh pair; clear the cooldown on that row, not the stale
  snapshot (which wrote the expired access token back and resolved it).

Verified: tests/hermes_cli/test_config_set_preserves_comments.py test_config_yaml_comment_preservation.py
test_config_set_list_values.py -> 29 passed; test_config_set_coercion.py test_config_set_platforms_redirect.py
-> 20 passed; tests/hermes_cli/test_auth_codex_quota_probe.py test_auth_codex_provider.py -> green;
tests/agent/test_codex_owner_*: unchanged red set (needs L2's hermes_cli/auth.py _auth_lock_path).

* test(parity 2026-10-01 ci): M2-hermes-clia batch 1 — shell-hook coverage, anon auth, codex self-heal

- test_agent_host_shell_hook_coverage: patch discover_mcp_tools where main.py now imports it
  (tools.mcp_tool_discovery; the tools.mcp_tool re-export went with the compat layer in a5bd246865b)
  and map upstream's extracted gateway/run_turn*.py + slash_commands_session.py to the GATEWAY host.
- test_anon_auth_core (upstream-only): a successful _swap_credential returns the fork's
  SwapOutcome.SWAPPED, not bare True.
- test_auth_codex_self_heal #73667 tests (upstream-only): the fork's Codex owner store (#673) serves
  a singleton with an access_token before the singleton read, so exercise the CLI recovery through
  the rejected-refresh path (_refresh_codex_auth_tokens) with the same CAS / workspace assertions.

* fix(parity 2026-10-01 ci): L5 cron list default set, lazy provider catalog, web_server facade symbol

- hermes_cli/cron.py: `cron list` asks the store for include_disabled=show_all (fork --json
  contract) and tops the human table up with paused jobs (upstream 3f399c0bd4 contract).
- hermes_cli/models_catalog_static.py: drop the import-time sync_plugin_provider_catalog():
  list_providers() imports plugins that import hermes_cli.models -> partially initialised
  module (fork lazy auto-extend contract; the post-discovery sync hook still runs it).
- hermes_cli/web_server.py: module-level get_hermes_home facade symbol (tests patch it).
- tests: desktop cron ticker test admits the fork can_dispatch kwarg (#1373);
  gpt-6.1-sol IS -900k eligible on the fork (#1550, measured 922k) - upstream assertion flipped.

Verified: tests/hermes_cli/test_cron_list_json.py test_dashboard_state_db_log.py
test_desktop_cron_ticker_gateway_standdown.py -> 18 passed; tests/hermes_cli/test_cron.py -> 29 passed;
tests/hermes_cli/test_lazy_canonical_providers.py test_gpt6_tiers_registration.py
test_list_picker_providers.py -> 41 passed; test_api_key_providers.py test_provider_groups.py green.

* fix(parity 2026-10-01 ci): M1 — re-thread track_agent persist + wecom final-frame tail; F02c vocabulary on injection test

- gateway/run_turn.py _run_agent_track_agent: restore the fork self._persist_active_agents() after the sentinel->agent promotion (2026-09-19 regression guard); upstream extracted the closure and the merge kept only the upstream body.
- plugins/platforms/wecom/streaming.py _send_stream_reply: final frame keeps head AND tail (t_11223645) — upstream split the adapter, merge took head-only.
- tests: track_agent AST test follows the extraction (run_turn.py, tuple-assign form); injection ack test asserts the F02c True/False/None seam, ended-session drop is owned by test_completion_delivery.

* fix(parity 2026-10-01 ci): M5 lane — ci + lcm smoke reds

- tests/ci/test_evaluate_needs.py: upstream dropped the pyyaml dependency
  (284dbaf5370, YAML unified on ruamel); read ci.yaml via hermes_yaml.
- tests/ci/source_proxy_baseline.json: ratchet re-baselined on the merged
  test set — 48 entries whose tests the merge deleted/rewrote removed,
  the fork's relocated tests/run_agent -> tests/agent fallback-override
  key re-keyed, three upstream-authored pre-existing proxies and the L2
  AST-walk rewrite of test_every_fallback_activation_restarts_preflight
  frozen (pre-existing, not endorsed; count 114 -> 67).
- scripts/probe_hermes_lcm_isolated.py: compare live roots lexically; the
  merged tests/home_io_guard refuses realpath() under the real home, so
  resolving ~/.hermes/plugins to decide a refusal tripped the guard on
  every smoke test. Only the candidate path is resolved now.

Verified: tests/ci/test_evaluate_needs.py + test_no_new_source_proxy_asserts.py
+ tests/context_engine/test_lcm_adoption_smoke.py 32 passed under e45-pt;
smoke 6/6 under a non-temp HERMES_HOME (6/6 fail without the probe change).

* fix(parity 2026-10-01 ci): M1 — yaml shim in slash_commands, completion silence hint on the extracted formatter

- gateway/slash_commands.py: two raw `import yaml` -> `import hermes_yaml as yaml` (CI venv has no PyYAML; the fork reads YAML through hermes_yaml).
- tools/process_registry_notifications.py: upstream extracted format_process_notification and the copy dropped the fork COMPLETION_SILENCE_HINT tail (Ace contract 2026-09-27); constant now lives in the notifications module, re-exported from tools.process_registry.
- tests: yaml shim in two tests; compress locale sweep skips upstream *.tui.yaml catalogs; goal wait_on test stubs upstream _pid_alive; fast_command provider doubles accept target_model=.

* fix(parity 2026-10-01 ci): M5 lane — acp_adapter reds

- agent/conversation_compression_manual.py: compress_now passes
  trigger_reason="manual_compress_command" (same 3-line change as the
  unfolded L1 lane, cb0f5645c50; applied here so the ACP test is green on
  this branch regardless of fold order — identical bytes, folds clean).
- tests/acp_adapter/test_session.py: get_messages_as_conversation(
  include_timestamp=True) — the fork's default projection is byte-stable
  (F01), same adaptation L2 made for test_inline_edit_persistence.

Verified: tests/acp_adapter/test_server.py + test_session.py 59 passed (e45-pt).

* fix(parity 2026-10-01 ci): M2-hermes-clia batch 2 — codex owner row identity, auth refresh outcome, kimi/pool fixtures

- agent/codex_owner._current: compare the hydrated PooledCredential.source, not the raw row column:
  a row written without `source` loads as SOURCE_MANUAL on both sides and is the same generation
  (upstream's profile-shadowing test in test_auth_profile_fallback seeds such rows).
- hermes_cli/auth_commands.auth_refresh_command: the fork's Codex owner store raises AuthError on
  a failed/uncertain refresh instead of returning None; map it to the same "Could not renew" exit.
- test_auth_kimi_oauth_provider: drop the load_pool->None stub; the merged _add_kimi_oauth_credential
  (upstream shape) reads the re-seeded pool entry.
- test_auth_pool_operations (upstream-only): rows start unbenched (the owner store refuses a forced
  refresh of a cooldown row) and a non-429 failure is asserted as the owner's receipt fence
  (dead / codex_refresh_uncertain, pair untouched) rather than upstream's exhausted/dead split.

* fix(parity 2026-10-01 ci): restore the confab tool-call notice recovery on the extracted turn loop

The 2026-10-01 merge kept upstream's extracted agent/turn_*.py siblings and dropped three fork
seams (#942, agent/confab_notice.py) that lived inline in conversation_loop.run_conversation:

- turn_response_intake.normalize_model_response: announce a validated out-of-band notice once
  per turn (should_announce_notice ledger) and carry _confab_notice/_new_confab_notice on the
  verdict (+ _LoopState slots) for the final-text phase.
- turn_final_response.finish_text_response: tool_call_as_text / tool_call_unparseable notices
  re-prompt with the fixed TOOL_CALL_NOTICE_TEXT instruction BEFORE the empty-response ladder,
  persist the metadata-only system event row, share the 3-stall dropped-tool-call budget, and
  end the turn failed (tool_call_recovery_exhausted) when it is spent. Without this the merged
  loop fell into the empty-response retry (the StopIteration lead signature: one extra provider
  call per scripted notice across 137 reds).
- turn_context.build_api_messages: metadata-only notice rows never reach the provider request.

Fixture/assert updates in tests/agent/test_confab_notice_e2e.py for upstream mechanisms the
merge legitimately adopted (evidence in the CI5 ledger): 0f4587e336f first-request preflight
deferral (arm the built-in compressor with a real-usage anchor; the mock never prices the
transcript), #118900 last-assistant-reply re-anchor (pin the notice's neighbour, not list
length), 8f0322da5b8 failed_turn boundary row (exclude the Hermes-authored row).

Verified: tests/agent/test_confab_notice_compaction_stats.py 1/1; test_confab_notice_e2e.py
lcm shards + valid_empty_tool_event_is_not_replayed green after the turn_context port; full
file re-proof in progress.

(cherry picked from commit 0813ef705ecbfb71cf939685ee1dee725dd0ad19)

* fix(parity 2026-10-01 ci): M4-tools — approval bypass facade, async-delegation retirement/prune guards, formatter re-export

- tools/approval.py: every bypass site goes through is_approval_bypass_active() again (the merge
  re-introduced a hand-rolled `_yolo_active()` that dropped approvals.mode=off — the fork 2026-09-08
  incident); `_get_approval_mode` is a facade delegate so tests can patch either module.
- tools/async_delegation.py: `_dispatch_admitted` tolerates an executor double returning no future
  (fork test doubles) instead of rejecting as submission_failed; `_prune_completed_locked` no longer
  hashes a runner-supplied status (fork hostile-__hash__ guard).
- tools/process_registry.py: re-export `_format_async_delegation` (fork facade symbol upstream moved).
- tests/tools/test_async_delegation_terminal_receipts.py: dispatch helper assertion carries the payload.

Verified narrowly via scripts/test-gate: test_approval_mode_off_bypass + approval_mode_parity + test_approval
174 passed; numeric_binding_boundary + registry_boundaries + terminal_receipts 204 passed.

* fix(parity 2026-10-01 ci): M1 — discord native slash Range fallback + omitted-option defaults; reasoning descriptions name max/ultra

- plugins/platforms/discord/adapter.py _slash_proxy: resolve app_commands.Range tolerantly (test stubs lack it; bare type keeps the option) and render omitted optional options from the synthesised signature defaults (/undo with no count -> "/undo 1", the fork t_b4f07acf contract). Clears 14 reds in test_discord_slash_commands + 15 connect/liveness/event_silence reds that failed at connect() on the same AttributeError.
- locales/en.yaml platform.discord.command.reasoning: description/arg_effort name the effort levels incl. max + ultra (fork contract; the i18n key form dropped them). gateway/relay/command_manifest.py carries a hardcoded copy — out of lane, FOLLOWUP.
- tests: free_response offload class adapted to the fork sync mark_many + CoalescingJsonWriter seam (fork design; upstream-only tests); producer_matrix seeders import from cron.scheduler_delivery (upstream extraction), capture double stamps the admit_internal_event receipt, F02c True vocabulary.

* fix(parity 2026-10-01 ci): M3-agentb round 1 — re-thread stop-gate, placeholder seam, preflight announce; repoint moved patch targets

- agent/turn_stop_gates.py: kanban stop guard threads tools= into
  build_kanban_stop_nudge; persists the candidate answer (interim flush) and
  flags only the nudge synthetic (t_4eeb0202).
- agent/turn_final_response.py: classify_placeholder_final_text wired at the
  final-text seam before the empty ladder (t_887f9584); streamed buffer cleared.
- agent/turn_context_compaction.py: engine preflight maintenance announce with
  reason template + preflight_is_user_visible gate (fork 5636a8a6d5/829d4e0f3e).
- tests: iteration-limit summary drives agent._interruptible_api_call (summary
  now goes through _build_api_kwargs); model_metadata patches
  model_metadata_http.get/.stream, gpt-6 sol/luna 900K cap w/ catalog max,
  bedrock cache row via bedrock_confirmed provenance; kanban_stop source grep ->
  turn_stop_gates + complete_task result=; _FALLBACK_ANNOUNCE_LABELS rename.

Verified: scripts/test-gate narrow runs — test_iteration_limit_summary_session_user 3 passed,
test_kanban_stop 54 passed, test_model_metadata 158 passed,
test_placeholder_final_text_backstop 17 passed, test_preflight_announce_visibility +
test_pool_exhaustion_scope_label 22 passed.

* fix(parity 2026-10-01 ci): M1-gatewayb batch 1 - hermes_yaml shim, refused-followup report at adapter drop

- gateway/slash_commands.py: two lazy `import yaml` sites -> `import hermes_yaml as yaml`
  (upstream dropped pyyaml from deps; the fork shim is the loader everywhere else).
- tests: test_switch_announce, test_turn_concurrency, test_stop_during_preflight_refuses_turn
  import hermes_yaml (safe_dump, no `dump`).
- gateway/platforms/base.py::_drop_unresolved: a replayed restart follow-up refused by the
  adapter-level profile-route gate (which runs BEFORE the runner ingress gate upstream added)
  now logs PHASE=restart_followup_lost; the log body moved to
  fork_ext/restart_followups.report_refused_followup and run.py delegates to it.

Verified: test_switch_announce+test_stop_during_preflight+test_turn_concurrency 75 passed
(1 local-only home_io_guard false positive under the lane runner, CI-shaped run green);
test_restart_followups_admission_e2e 18 passed.

* fix(parity 2026-10-01 ci): M2-hermes-clia batch 3 — backup disk-image/usage-ledger excludes, CLI receipt batch

- hermes_cli/backup.py: restore the fork's staging disk-image suffix exclusion (.sparseimage /
  .sparsebundle / .dmg; 2026-08-09 42 GB subvps-staging.sparseimage) onto upstream's suffix tuple,
  and keep cache/claude-usage (usage.ace ledger) in the kept cache subdirs.
- test_backup: the root cache/*.MOVED.txt breadcrumb now falls under upstream's regenerable-cache
  rule (same verdict as cache/model_catalog.json); kept-dir assertion carried.
- test_cli_async_delegation_delivery (fork test relocated by upstream's tests/ mirror): completions
  arrive wrapped in ProcessNotificationBatch; unwrap before asserting the accepted input + sibling.

* fix(parity 2026-10-01 ci): M4-tools — HERMES_ALLOW_REBOOT downgrade, sandbox session-id bridge, lifecycle refusal markers

- tools/approval_detection.py: restore the fork HERMES_ALLOW_REBOOT opt-in (reboot/shutdown family
  downgrades from hardline to the DANGEROUS layer; every other hardline pattern untouched). Dropped when
  upstream extracted detection out of tools/approval.py.
- tools/code_execution_env.py: re-thread the fork `_inject_session_id` tail of `_scrub_child_env`
  (#636/C3: contextvar-resolved HERMES_SESSION_ID into the sandbox child; removed when unresolvable).
  Resolver stays on the facade (tools.code_execution_tool._resolved_session_id).
- tools/code_execution_tool.py: re-export `_scrub_child_env` / `_HERMES_CHILD_ALLOWED` (fork facade symbols).
- tools/terminal_tool_guards.py: every gateway-lifecycle refusal carries `blocked_by` =
  GATEWAY_LIFECYCLE_BLOCK_MARKER again (fork: execute_code surfaces blocks instead of a silent 0-exit).
  `_blocked_json` gains an optional `blocked_by`.
- tests/tools/test_execute_code_surfaces_blocks.py: source scan repointed to the extracted
  gateway_lifecycle_block (counts `_blocked_json(` vs markers). RED-proofed: unstamping one refusal fails it.

Verified via scripts/test-gate: test_hardline_blocklist 263 passed; test_execute_code_session_provenance +
test_execute_code_surfaces_blocks 18+11 passed.

* fix(parity 2026-10-01 ci): agent_init tool loader honors both patch contracts (run_agent.* and model_tools.*)

The L7 fold made _load_tools read get_tool_definitions/check_toolset_requirements
through the run_agent facade so fork tests patching run_agent.* work, which
shadowed the 142 upstream-style tests patching model_tools.* (a real catalog
loaded under the mock -> 'clarify' first instead of 'terminal'/'web_search').
_tool_catalog_fn prefers a patched facade attribute, else reads model_tools.

test_primary_runtime_restore: FailoverReason import from agent.error_classifier
(run_agent no longer re-exports it upstream; sole test importer).

Verified: test_run_agent_codex_responses + test_provider_parity +
test_primary_runtime_restore 140 passed; tests/cron/test_cron_fallback_alert_e2e
(run_agent.* patch convention) still green.

* fix(parity 2026-10-01 ci): M3-agenta — compaction attribution, codex ctx facade, credential-pool + anchor seams

agent/model_metadata.py: restore the fork's _resolve_codex_oauth_context_length
compatibility wrapper (upstream kept only the _with_source form; fork callers
and tests resolve through the bare name).

Compaction trigger attribution (cherry-pick of L1 cb0f5645c50 onto the fold
head, applies clean): trigger_reason labels re-threaded onto the extracted
turn_preflight / turn_context_compaction / turn_overflow / turn_recovery /
conversation_compression_manual / gateway run + run_turn call sites. Also
cherry-picked L1 0813ef705ec (confab tool-call notice recovery on the
extracted loop; one trivial union in turn_context.build_api_messages with the
fold's interrupt-close omission) — it clears the 32 confab-matrix
StopIteration reds in this manifest.

Tests adapted to seams the merge legitimately adopted (evidence in the CI6
ledger): compaction lint/announce follow the call sites into the turn_*.py
modules and the CompressionFacadeMixin forwarder; Platform is no longer a
prompt-identity field (#104414, agent/surface_switch.py); the feasibility
probe reuses the main window on a shared route (#89500) so the compressor
double carries context_length/threshold_tokens; model_metadata.requests ->
model_metadata_http.get; xai auth-store sync lives on the consolidated
_sync_entry_from_auth_store; Nous forced refresh adopts a peer-rotated usable
key without redeeming the grant (a6f75130386) while the fork's #670 stale
refusal still redeems the pool token; the fork gates timestamp behind
include_timestamp (#107) and rolls back compactions that INTRODUCE duplicate
active tool results (t_aace5343) — the anchor fixture opts in and persists
the tool rounds the way the loop's per-round flush does. The two real-lcm.db
replay oracles are marked allow_real_home_io (read-only, skip when absent).

Verified narrowly via scripts/test-gate (<=3 files/call):
  codex_subscription_proxy_context + gpt61_sol_900k + gpt61_sol_prestage: 31 passed
  compaction_attribution_lint + compaction_fallback_prompt_identity +
    compaction_stats_reconcile: 112 passed, 2 skipped
  credential_pool_singleton_freshness: 30 passed
  compression_last_assistant_anchor: 8 passed

* fix(parity 2026-10-01 ci): M5 lane — cron reds (11 files)

Code:
- cron/scheduler.py _resolve_job_fallback_chain: a job pinned by MODEL or
  ENDPOINT alone (no provider) never borrows the global chain (upstream
  #100437 / scoped_fallback_chain); the fork's same-provider filter st…
Kyzcreig added a commit that referenced this pull request Oct 4, 2026
… fixes main-red e2e-upgrade (t_c7d31a6a) (#1702)

* test(parity 2026-10-01 ci): completion_delivery pins the merged bool/None delivery vocabulary

Fork tests asserted the fork's "delivered"/"temporary"/"dropped" outcome
strings on _deliver_completion_notification / _deliver_async_delegation_group;
the merged seam (F02c ledger decision) is upstream's True/False/None contract
with the admit_internal_event receipt. Three mechanical axes:
- "delivered"->True, "temporary"->False, "dropped"->None on the seam asserts
- AsyncMock handle_message doubles -> AdmittingHandler (sets _gateway_accepted)
- _runner() session_store lends a _db (upstream #98573 borrows the store handle;
  without it runner._session_db is None and every pre-flight returns retry)
drop_requires_proven_terminal_target[False-*]: an unroutable event is False
(retryable), matching the test's own "not proof no future consumer can deliver"
and test_unroutable_async_event_remains_retryable.

Verified: gated pytest tests/gateway/test_completion_delivery.py ->
101 passed, 2 failed before the [False-*] fix; those two re-run below.

* fix(parity 2026-10-01 ci): re-thread fork follow-up spooling, /footer setter, session.redo, /model read-path flags

gateway/run_turn._run_agent_drain_pending: the merge took upstream's
"Discarding pending follow-up" at the extracted site, re-creating the fork's
2026-09-23 data-loss (4 follow-ups lost). Restore the draining-gateway spool
(_preserve_followup_across_restart) at the moved site.

gateway/run_startup._drain_startup_restore_queue: use the fork seam
_adapter_for_source (intake first, then unique (platform, profile) owner).
Upstream's _intake_adapter_for fails closed on spooled restart follow-ups
(restored rows, no live provenance) under multiplexing -> retried forever.

gateway/run_notifications: reconcile the two parallel fixes for a stale
update notice. Fork policy stays (24h configured / 5min unconfigured); a
runner with NO config object gets upstream's flat 1h cap (601a8a17c22) and
upstream's "adapter never connected" wording.

tui_gateway/methods_config_set: fork /footer config.set branch
(display.runtime_footer.enabled) dropped when upstream tabled the if-chain;
restored as a _CONFIG_SETTERS entry.

tui_gateway/methods_session: fork session.redo RPC route restored beside
session.undo (core _redo_session_core already survived in server.py).

gateway/slash_commands (/model picker, fork handler kept by R09): import
list_picker_providers from model_switch_providers and pass upstream #74003
read-path flags (cache-only catalogs, probe only the current custom endpoint).

Verified (hermetic test-gate, CI-faithful harness without pre-set HERMES_HOME):
  tests/gateway/test_update_command.py + test_restart_interrupt_intent_followups.py
    (+ test_restart_cascade.py) 115 passed, 5 skipped, 3 failed (cascade: separate)
  tests/gateway/test_restart_followups_boot_replay_e2e.py 3 passed (in c1 batch 35 passed)
  tests/gateway/test_model_command_async_offload.py + test_notify_sub_chat_type_write_canonicalization.py
    (+ test_multiplex_routing_authz.py) 16 passed, 1 failed (multiplex: separate)
  tests/tui_gateway/test_desktop_runtime_footer.py 10 passed (tui2 batch)

* test(parity 2026-10-01 ci): repoint source anchors and doubles at upstream's moved seams (gateway/tui)

- test_notify_sub_chat_type_write_canonicalization: writer reads fields via
  _field(); metadata branch gained `and chat_type`. Behaviour (chat_type
  resolved before the branch, canonical on write) unchanged.
- test_restart_interrupt_intent_followups: draining branch moved to
  run_turn._run_agent_drain_pending; anchor there, end at the method return.
- test_desktop_runtime_footer: payload literal moved to
  prompt_turn._complete_turn_payload; anchor on the server.py call site that
  attaches payload["footer"].
- test_undo_redo: _start_inflight_turn double accepts upstream's
  display_kind/display_metadata kwargs; fixture clears
  server_requests.reset_for_tests() (server._pending/_answers retired).
- test_server_no_duplicate_defs: upstream #97948 replaced the fixed 120s
  compress wait with the config-derived budget (floored at 120s); assert the
  budget the real helper computes reaches the supervisor.

* fix(parity 2026-10-01 ci): restore D-6 finally-block breadcrumb consume in run_turn; cascade guards scan the split turn pipeline

gateway/run_turn.py: the handler's finally block consumes the restart-initiated breadcrumb again (fork/main run.py:28913, dropped when upstream split run.py into run_turn*.py).
tests/gateway/test_restart_cascade.py: the three source-scan guards concatenate run.py + run_turn.py + run_turn_runner.py so the relocated callback/gate/finally are still covered.

Verified: e45-pt-L3b.sh tests/gateway/test_restart_cascade.py -k 'c1_detection or single_gate or finally_consume' -> 3 passed.

* fix(parity 2026-10-01 ci): L4-kanban round 2 — reaper owner window, corrupt-board guard, review schemas; ledger

Code:
- kanban_db_dispatch._reap_terminal_worker_row: pass the fork's required owner_window/conn/run_id
  (merged call raised TypeError inside the per-row guard -> terminal workers never reaped).
- gateway.kanban_watchers._is_corrupt_board_db_error: read KanbanDbCorruptError from
  kanban_db_connect (facade no longer re-exports it; quarantine was disarmed).
- tools/kanban_tools_schemas: kanban_request_changes description restored (lens list from
  kanban_review_schema).

Tests (upstream-only fixtures -> fork contracts):
- test_kanban_db: stale-claim breaker models a dead claimer past the launch bound; infra spawn
  refusal marked platforms("linux") (restart_safe_gateway_child_argv is in_process off Linux);
  archive termination event is the fork's archive_worker_terminated.
- kanban_tools late-orphan: dead claimer + launch bound; _pid_started_in_claim seam.
- worker_authority_isolation: pin the RUN id, structured metadata for the receipt gate.
- review_coverage_gate human lane: drop worker scope, home the card on the reviewing session.
- second_claim_class [complete]: operator close under a live claim is force=True (#111764).
- schedule_wake: promote force= retired (#106195). survivor/termination_identity: #117483 evidence.
- worktree_teardown retry: patch kanban_survivor._git.

Ledger: docs/sync/review/ledger-2026-10-01/CI5-L4-kanban.md

* fix(parity 2026-10-01 ci): restore fork behaviours dropped by the merge, round 2 (L2-agent-core)

- codex_owner.refresh: a 429 refresh POST reserves the token's probe-throttle
  slot (new hermes_cli.auth_codex._reserve_codex_quota_probe_slot) so the
  mid-cooldown pre-probe refresh (#89415) does not re-POST the same single-use
  refresh token on the very next selection (test_codex_owner_selection_review
  saw two POSTs).
- agent_init._resolve_context_length: model.max_tokens config override
  (fork; dropped in the phase-helper split) re-threaded onto agent.max_tokens
  and _session_init_model_config.
- turn_usage: compressor update gated on a MEASURED prompt count read from the
  pre-fold aggregator usage (r6 findings 7 / round-4 2), plus the one-shot
  Codex 272K tier notice (#1567) — both lived in the fork's conversation_loop
  usage block.
- turn_finalizer: _diag_msg/_diag_args keep the fork source contract (end on
  task=/effective_task_id); the upstream origin= tag rides as a suffix.
- shell_hooks: spawn EACCES/ENOENT named exactly without the OSError detail
  (argv can be the credential); unparseable/empty stdout is its own fail-closed
  reason ("unparseable stdout"), distinct from an unknown directive.
- provider_seam._restore: carry forward containers registered after the
  snapshot (lazy facades such as HERMES_OVERLAYS) instead of leaving them out
  of the swapped generation (KeyError on every later read).
- honcho identity_signature: memo keyed on honcho.json bytes, not mtime
  (coarse-mtime filesystems served the stale pinPeerName; fork C7 k102).

Verified narrowly via e45-pt-L2.sh (sandboxed test-gate), each file green
after its fix: test_codex_owner_selection_review, test_credential_pool,
test_run_agent_init_memory, test_usageless_response_accounting,
test_codex_tier_notice, test_turn_ended_task_id_log, test_shell_hooks,
test_plugin_transport_api_mode, test_auth_registry_mid_discovery,
test_oauth_pkce_plugin, test_provider_registry, test_plugin_discovery,
test_pin_peer_name, test_identity_signature.

* test(parity 2026-10-01 ci): meet fork contracts in merged tests, round 2 (L2-agent-core)

- entitlement_fail_closed / non_chat_primary_restore: assert the fork's
  gated route announcement ("Model recovery (restore): ...",
  model.announce_recovery) instead of upstream's removed unconditional
  "Primary model restored" notice.
- credential_pool_codex_singleton_isolation: a manual:device_code row never
  adopts the singleton (agent/codex_owner, #673); the re-auth reaches the pool
  through the seeded device_code row.
- inline_edit_persistence / replay_cleanup: get_messages_as_conversation(
  include_timestamp=True) (fork F01 default projection is byte-stable);
  _SendAgent double carries provider for the interrupt-close filter.
- prompt_cache_ttl_propagation: #84733 restart-discipline guard rewritten for
  the phase-helper split — every _try_activate_fallback site in agent/turn_*
  must be an `if` test whose verdict is "break" (retry-loop phases, via
  _arm_fallback_restart / _fallback_break) or "continue" (outer-loop phases).
  Mutation-checked: flipping recover_empty_response to "break" fails it.
- route_id_correlation: fake Anthropic stream yields message_stop (upstream
  _drain_stream treats a stream without it as a drop).
- usage_pricing: models.dev leg exercised with an id absent from the fork's
  Grok snapshot; xai-oauth and api.x.ai-over-HTTPS price at xAI list rates by
  fork design (notional relay / host match).
- provider registry fixtures (plugin_transport_api_mode,
  auth_registry_mid_discovery, oauth_pkce_plugin): teardown via
  provider_seam._restore / _reset — the facades are additive and refuse
  pop/clear.
- run_agent_api_kwargs: the fresh-build assertion is checked before
  _build_system_prompt (upstream 921ab7a163 seeds the workspace pin from the
  session row inside the build).
- pin_peer_name: Honcho keys ride memory.<key> via identity_signature().
- plugin_paths_follow_profile: drop the mem0-qdrant case (fork retired the
  mem0 OSS backend; docs/sync/review/mem0-resolution-decision.md).
- credential_pool codex_sync_pool stub: httpx.SyncByteStream for the
  response-body cap; curator_disk_accounting hands the review a candidate
  list; system_prompt patches the prompt_builder symbols;
  pool_capacity_503_retry rig disables streaming (upstream c5b99a3ee5 keeps
  direct-call contexts on the streaming wire).

* fix(parity 2026-10-01 ci): L3a gateway a-m batch 1 — honcho memo digest, checkout gate ordering, boot preload roots, stale fixtures

Code:
- plugins/memory/honcho: identity_signature memoizes on the config's CONTENT digest and only
  stores values when the bytes it parsed still match (fork C7 k102; mtime/size key served stale
  values on coarse-mtime filesystems).
- tui_gateway/server._run_prompt_submit: checkout gate (fork C6 #1035) runs BEFORE the merged
  ownership/liveness admission so a frozen continuation is refused with no lease/inflight/agent
  side effects.
- gateway/boot_preload: root modules derived from the tree like upstream setup.py (py-modules
  list is gone); hermes_platform + pm packages preloaded; 4 import-side-effect scripts excluded.
- api_server_openai_routes: history loader passes include_timestamp=True (fork #107 shape).

Tests adapted to merged contracts (reasons inline + ledger):
auto_continue (interrupted marker last line), auto_skill_title (run generation gate),
background_process_notifications (bool delivery, suppress_completion double), c7 backfill
(renamed pid probe, honcho seam), checkout_admission (room-grant token, wire-contract params),
clarify batch (TurnRunner._clarify_callback_sync owns the per-card loop).

Verified: narrow gated pytest on the 7 files — 56 passed (auto_continue/auto_skill/bg_notif),
82 passed (c7/checkout/clarify after fix; checkout_admission 56 passed alone).

* test(parity 2026-10-01 ci): multiplex preflight unpins the sandbox DB; undo_redo restores server in place; toolsets denylist checks the uncollapsed catalog

- test_multiplex_routing_authz: the fork's hermetic conftest pins hermes_state.DEFAULT_DB_PATH (gateway.session imports hermes_state), which wins over the profile scope inside _default_db_path(); restore the import-time sentinel (test_housekeeping_profile_scope idiom).
- test_undo_redo: importlib.reload(server) re-binds the split siblings onto an already-tagged namespace and trips upstream's bind_module collision guard (change_watcher._active_pet vs methods_session._active_pet); restore _methods in place like test_undo_command.
- test_gui_surface_toolsets: upstream renamed todo -> todo_list and defers it behind the tool_search bridge; assert the denylist on the uncollapsed catalog and on the assembled schema.

Verified: e45-pt-L3b.sh on the three files -> 19 passed + 4 passed (undo_redo).

* fix(parity 2026-10-01 ci): L8 state/ci/scripts reds (batch 2) + ledger

CODE:
- gateway/session_persistence: _write_sessions_json_unlocked moved onto the
  mixin next to its only caller _save_sessions_json (merge left the mixin
  calling a SessionStore-only method; upstream's writer test instantiates the
  mixin alone -> AttributeError).
- gateway/platforms/base: upstream names _MACOS_PROXY_TTL_SECONDS /
  reset_macos_proxy_cache alias the fork's stale-while-revalidate cache.
- 17 upstream-only files: monotonic limiter seeds 0/0.0 -> float("-inf")
  (fork lint 2c2adef9ed3 now sees upstream code; 22 hits -> 0). Guarded unset
  sentinels carry `# zero-seed-ok:` naming the guard.
- publish_evidence_step.sh + its test removed: upstream 2ab7d9bfe30 deleted
  the publish-e2e-evidence pipeline the wrapper served (no caller left).

TESTS (fork contract vs upstream fixture):
- rewind_surfaces_invariant: gateway /undo N and undo_last(N>1) count
  half-turns (fork); harness passes 2*n, invariant unchanged.
- writer_conn_thread_safety: fork retries message-scoped SystemError; test
  asserts bounded replay then propagation.
- session_list_denorm_reland: AST contracts scan the hermes_state* mixin
  family + _INSERT_MESSAGE_SQL callers.
- test_hermes_state_core v9->v11 fixture session titled (v16 orphan tagging,
  v30 delegate exclusion from trigram).
- detect_changes_event_scoping: docker/nix are path-scoped lanes upstream.
- atomic_json_writers_unified: .sessions.lock is the fork writer lock, not a
  temp file.
- planned_restart_notice_multiplex: two inert tuple assertions.
- tests/agent/conftest: _block_real_claude_keychain honours
  allow_macos_keychain (upstream Keychain tests run the Darwin branch with
  subprocess.run mocked).
- snapshot_session_id_leak: HERMES_HOME is scoped in the fork (#543).

Verified (test-gate, sandboxed HOME, .venv python): every manifest file +
the 3 macOS-job files green narrowly; details per file in
docs/sync/review/ledger-2026-10-01/CI5-L8-state-ci-scripts-root.md.

* docs(parity 2026-10-01 ci): CI5 ledger for lane L2-agent-core

* fix(parity 2026-10-01 ci): hygiene keeps the fork's 400-message hard limit and repeated-failure escalation; /stop background test pins the catalog key

gateway/run_turn.py + run.py: _HygieneSettings defaults hard_msg_limit=400 (fork fleet default, config_defaults.py; upstream 5000) and carries failure_alert_after (compression.hygiene_failure_alert_after). The timeout and abort notices bump the per-session streak and, from the Nth consecutive failure, send agent.hygiene_timeout.format_repeated_failure_alert; a landed compaction clears it. Fork behaviour from run.py's pre-split hygiene block (fork/main 27499-27560, 27840).
tests/gateway/test_session_hygiene.py: user-facing wording assertions read the i18n catalog (gateway.compress.hygiene_timeout / hygiene_failed) instead of the pre-i18n literals.
tests/gateway/test_stop_ends_background_delegations.py (upstream-only): the reply is compared to t('gateway.stop.stopped'); the fork's catalog words it present-progressive (test_stop_honest_wording.py).

Verified: e45-pt-L3b.sh test_session_hygiene.py -> 45 passed; test_hygiene_turnhold_backoff.py + test_hygiene_warning_lifecycle.py + test_stop_ends_background_delegations.py -> 7 passed.

* fix(parity 2026-10-01 ci): L3a gateway a-m batch 2 — route anchors, env bridge funnel, slash echo fields, flush helpers, override persist ordering

Code (merge regressions, fork behaviour restored onto upstream's structure):
- gateway/run: _bridge_config_to_env re-wires restart_policy._bridge_agent_config_to_env (config
  wins over stale .env for resume/restart knobs); _set_session_vars_for_source binds
  parent_chat_id (upstream a247012dc11) so a stale env never shadows the live source.
- gateway/platforms/event: MessageEvent regains suppress_public_echo / deferred_reply_text
  (Discord native-slash one-delivery handoff; base.py read them via getattr).
- agent/session_persistence: the fork flush helpers live beside the flush instead of a lazy
  `from run_agent import` that fails closed when run_agent is swapped (rows silently unwritten);
  run_agent re-exports them.
- gateway/session.set_model_override: persist OUTSIDE _lock (the fork _StoreLock defers in-lock
  writes to release, inverting publish-after-persist), then chat pin, then publish.

Tests adapted (reasons inline + ledger CI5-L3a-gateway-a-m.md): display_null wiring doubles,
kanban fair_dispatch + dispatcher_standby (kanban_db_dispatch / kanban_watchers_common seams),
login_command (route identity carries provider; rehydrate resolves as-is), loop_liveness
(patch gateway.shutdown_watchdog).

Verified (narrow gated pytest): display_null+internal_row+kanban 28 passed; route_anchor+login+
loop_liveness 47 passed; login+chat_model_pins+model_override_persistence 47 passed;
session_store_lock_io/never_spans_io green; interrupt_close_flag + flush_diverts green.

* fix(parity 2026-10-01 ci): L7 tools/cron — web keyed-fallback + breaker chain, delegate timeout transport drain, durable background output, kanban grant boundary

Round-5 CI lane L7-tools-cron for #1624 (ledger docs/sync/review/ledger-2026-10-01/CI5-L7-tools-cron.md).

Code restored onto upstream's structure (fork behaviour the merge dropped):
- tools/web_tools_extract.py: _breaker_extract (402/401 dead-backend breaker, #1562), _failed_extract_batch and the
  keyed web.extract_fallbacks chain (#1516) back in _dispatch_extract; hooks read via the tools.web_tools facade.
  tools/web_tools_truncate.py: _trim_results keeps served_by/fallback_from (+ local-pdf metadata).
- tools/terminal_tool_background.py: durable_output=bool(notify_on_complete) on spawn_local (t_1191e078).
- tools/terminal_tool.py: messaging-gateway turns keep the over-cap foreground REFUSAL (#1012).
- tools/terminal_tool_guards.py: process(action="wait") guidance copy in the nohup/& recipes.
- tools/code_execution_env.py: git-lane env carry (t_45c11886 / C3 #1254) re-threaded into the extracted module.
- tools/delegate_tool.py + delegate_tool_child_run.py: upstream's abandoned-worker transport drain (#94248) split out
  as _drain_abandoned_child_transports and called from the fork supervisor's timeout path.
- cron/lifecycle_guard.py: read-only heredoc data paths are not mention candidates (#1017/#1348).
- cron/scheduler.py: never suppress the alert that enters a provider-window quota hold (#89376).
- hermes_cli/cron.py: vanished-job warning on every status path.
- hermes_cli/kanban_db_dispatch.py: pop HERMES_DELEGATED_CHILD_CONTEXT at the grant boundary; _recent_worker_exits
  read through the kanban_db facade.
- tools/skill_manager_tool.py: name validator fullmatch (AC10).
- agent/agent_init.py + run_agent.py: tool definitions read through the run_agent facade.

Tests repointed to moved symbols / fork contracts (see ledger per file); tests/tools/test_lazy_sdk_probe_importable.py
deleted (tests the retired tools.lazy_deps surface; property pinned by tests/pm/test_extras.py).

Verified (e45-pt-L7.sh, sandboxed HOME, <=3 files per call): every manifest file green —
cron: fallback_alert 2/2, store_concurrency 14/14, no_auto_sentinel 8/8, workdir 16/16, fire_fence 3/3,
selfisolation 1/1, heredoc_data 28/28, python_heredoc_parity 66/66, quota_hold 4/4;
tools: blocked_command_guidance 8/8, browser_real_profile 65/65, code_execution_git_lane_env 1/1,
cron_auto_model 93/93, cron_model_arg_coercion 13/13, gateway_foreground_deafness 32/32, launchctl_guard_diagnostic 2/2,
request_tool_approval 13/13, skill_manager_create_shared 35/35, snapshot_session_id_leak 4/4, terminal_task_cwd 7/7,
timeout_transport_drain 4/4, web_backend_breaker 19/19, web_keyed_fallbacks 6/6, windows_native_support 16/16.
tests/cron/test_cron_kanban_env_isolation.py 15/15 on ace-ai (linux-only spawn test).

* test(parity 2026-10-01 ci): model-override rehydration tests follow the fork's identity re-resolution; pre-agent fallback fixture answers the route precheck

tests/gateway/test_session_model_override_persistence.py: upstream's three new tests patch _resolve_runtime_agent_kwargs_for_provider, the legacy identity-less path. On the fork a persisted {model, provider} identity is durability truth and is re-resolved through _reresolve_model_override_credentials -> hermes_cli.model_switch.switch_model, so the doubles move there. The codex still_unavailable arm now asserts the fork contract: SessionRouteUnavailableError, preference preserved, default route never consulted (same as test_session_model_reset.py credentials-unavailable) instead of upstream's silent default-provider fallback.
tests/gateway/test_pre_agent_fallback_notice.py (upstream-only): the MagicMock runner returns PersistedSessionRouteLookup('absent') from _persisted_session_route_identity so the fork's fail-closed precheck sees no pin.

Verified: e45-pt-L3b.sh test_session_model_override_persistence.py -> 9 passed; test_pre_agent_fallback_notice.py -> 2 passed.

* fix(parity 2026-10-01 ci): restore fork auth-error markers; telegram/warning-wiring guards follow upstream's seams

gateway/run.py: _GATEWAY_AUTH_ERROR_RE regains the fork's credential-resolution / Codex-OAuth / pool-exhausted / provider-not-configured markers (fork/main run.py:754-764) so those envelopes map to the auth reply instead of the generic retry message.
tests/gateway/test_telegram_noise_filter.py: the credential-resolution test asserts upstream's plain-language auth reply (sign-in + /login), same as its sibling.
tests/gateway/test_telegram_restart_parity.py: the AST guards treat _start_polling_mode (extracted from connect) as bootstrap, accept upstream's _cold_boot_drop_pending(is_reconnect=...) gate (False on reconnect, config knob on cold boot — AC-1/AC-2 prove it), and resolve a single-assignment local alias. Mutation-checked: flipping the network-error ladder to drop_pending_updates=True still fails both guards.
tests/gateway/test_warning_wiring_conservation.py (upstream-only): the context carries a live status adapter + run predicate and the status lane is patched at safe_schedule_threadsafe, where the fork's late-resolving status callback schedules.

Verified: e45-pt-L3b.sh test_telegram_noise_filter.py -> 168 passed; test_telegram_restart_parity.py -> 6 passed; test_warning_wiring_conservation.py -> 1 passed.

* fix(parity 2026-10-01 ci): discord native slash option descriptions are catalog keys

plugins/platforms/discord/adapter.py: the fork's /new, /reset, /undo, /compress, /usage, /help option descriptions move from literals to platform.discord.command.<cmd>.arg_* keys (upstream's test_discord_native_slash_specs_hold_catalog_keys_only requires every slot to resolve).
locales/*.yaml: the 7 keys added to en.yaml and, in English, to every other locale (tests/agent/test_i18n.py::test_catalog_keys_match_english requires key parity; t() falls back to English anyway).

Verified: e45-pt-L3b.sh tests/agent/test_i18n.py tests/gateway/test_platform_adapter_i18n.py -> 67 passed. test_discord_slash_commands.py/_scope.py: 14 failed before and after this change (L3a lane, untouched).

* test(parity 2026-10-01 ci): ws orphan resume ctx carries params; manual-reset tests pin the store handle and read upstream's config warning

tests/tui_gateway/test_ws_orphan_races.py (upstream-only): the eager-resume ctx carries params={} — the fork's build reads the client's declared source from the request.
tests/gateway/test_manual_reset_sticky_route.py: the SQLite test pins its handle through store._db (the store now resolves through hermes_state_registry.acquire(), so patching hermes_state.SessionDB no longer reaches it — rows landed in a different file, FK failure); the parse-warning test reads upstream's config_read_errors warning (path + problem line, no source snippet) instead of gateway.run's removed error=<ExcType> line.

Verified: e45-pt-L3b.sh test_ws_orphan_races.py -> 29 passed; test_manual_reset_sticky_route.py -> 25 passed.

* fix(parity 2026-10-01 ci): queued follow-up re-stamps the turn clock and keeps a leftover /steer; relocated-seam test fixes

gateway/run_turn.py: fork 2026-09-29 behaviour re-threaded into the split turn pipeline — (1) a queued follow-up recursing on the parent's slot re-stamps turn.started_ts / busy_ack_ts (only for the key this run claimed); (2) a result['pending_steer'] that loses the next turn to a queued follow-up is appended to the session's /queue overflow (same slash-command guard) instead of being dropped. From fork/main run.py 39217-39260, 39547-39552.
tests/gateway/test_relay_injection_egress_priming.py: _inject_watch_notification returns upstream's True on adapter acceptance (merged contract, ledger F02c) — the fork's 'delivered' string is gone.
tests/gateway/test_resume_inflight_guidance.py: the AST wiring guard reads gateway/run_turn_runner.py, where upstream moved the dispatch site.
tests/gateway/test_rich_sent_store_off_loop.py: waits for the fork's dedicated writer thread instead of racing it (record_async only enqueues).

Verified: e45-pt-L3b.sh on the four files -> 22 passed (rich_sent_store run twice: 2 passed both).

* fix(parity 2026-10-01 ci): weixin writes ride the FIFO lane again; hard-exit funnel fences the lanes

ContextTokenStore.set is sync and dispatches through _dispatch_weixin_json_write
(single FIFO lane, ordered, fenced at disconnect/exit) instead of upstream's
per-call to_thread + asyncio.Lock; _sync_buf_path restored for _save_sync_buf.
gateway.run._exit_after_graceful_shutdown calls fence_lanes_for_hard_exit
between lock release and the lifecycle stamp (os._exit skips the atexit fences).
tests/gateway/test_weixin.py repointed at the lane contract (fence to observe).

Verified narrowly: test_weixin_state_write_off_loop (14 passed),
test_weixin + test_weixin_typing green, test_shutdown_pending_flush_off_loop
production-exit-funnel tests green.

* fix(parity 2026-10-01 ci): restore the ordered runtime-status lane behind the public writer

gateway/status: _RUNTIME_STATUS_LANE (single worker) + _fence_runtime_status_lane
back; submit_runtime_status_write queues on the lane again (fork #817 contract:
write_runtime_status fences it first so a direct terminal write can never be
overtaken by an older deferred one). write_runtime_status_locked alias restored.
The lane sits in front of upstream's _RuntimeStatusWriter, which still does the
actual persistence.

tests: test_no_sync_work realpath counter ignores the frames tests/home_io_guard.py
issues from inside Path.resolve()'s single walk (fork-only harness, 2b98a469d39);
test_runtime_status_write_off_loop spies _prepare_runtime_status_update (the
merge step every writer passes through) instead of the removed
_write_runtime_status_unlocked.

Verified narrowly: test_no_sync_work_per_inbound_message 13 passed,
test_runtime_status_write_off_loop 9 passed.

* test(parity 2026-10-01 ci): network-reachability ratchet follows upstream's module split; CI5-L3b ledger

tests/gateway/test_no_network_reachable_from_loop.py:
- validate_requested_model spy -> hermes_cli.models_validate (moved).
- _compress_context non-vacuity probe -> agent/compression_facade.py (AIAgent mixin).
- REACHABLE_BASELINE re-frozen for the run.py / run_agent.py split: the walker
  reports one sink per coroutine and now terminates the same pre-existing chains
  at requests.get (resolve_runtime_provider>_get_model_config>
  _auto_detect_local_model, on fork/main too) under the new module names.
  Measured with the test's walker: fork/main c14e059f8f2 = 17 sites, lane head =
  18, none newly reachable; /model and /reset doors still absent.

docs/sync/review/ledger-2026-10-01/CI5-L3b-gateway-n-z-tui.md: one row per red file.

Verified: test_no_network_reachable_from_loop.py 15 passed (72s).

* fix(parity 2026-10-01 ci): L5 keychain flags on dock launch, escaped resume-title hint, cli_hint/codex test seams

- tools/bot_desktop/browser.py: dock_argv carries --password-store=basic --use-mock-keychain
  (fork guard tests/cli/test_chrome_launcher_keychain_guard.py covers every detached launch).
- hermes_cli/main_tui_launch.py: the `-c "<title>"` resume hint goes through cli_hint.hint_value
  (fork contract: titles with $HOME / `id` must not expand).
- tests/hermes_cli/test_cli_hint.py: patch site moved to main_tui_launch; repair helpers
  now live in hermes_state_repair; SessionDB(read_only=True) ctor kwarg on the fake.
- tests/hermes_cli/test_auth_codex_provider.py: pool force-refresh goes through the owner
  transaction (refresh_codex_oauth_pure), not load_pool().try_refresh_matching.

Verified: tests/cli/test_chrome_launcher_keychain_guard.py tests/hermes_cli/test_cli_hint.py
tests/hermes_cli/test_auth_codex_provider.py -> 148 passed.

* parity(2026-10-01): L7 fold — re-apply the abandoned-worker transport drain call dropped by the import-conflict resolution

* fix(parity 2026-10-01 ci): restore compaction trigger attribution on the extracted turn modules

The fork's 2026-08-20 trigger-attribution audit passed trigger_reason=<label>
at every _compress_context call site (threshold / pre_api_pressure /
overflow_413 / overflow_context / tier_reduction / idle_resume /
engine_preflight_maintenance / session_hygiene / manual_compress_command).
Upstream's turn_*.py extraction dropped every label, so each compaction logged
trigger=UNATTRIBUTED and the announce rendered no reason clause.

Re-thread the labels onto turn_preflight, turn_context_compaction,
turn_overflow (compress/compress_scored_by_tokens gain a trigger_reason kwarg),
turn_recovery (tier_reduction), gateway/run_turn + gateway/run (session
hygiene), and the shared manual core conversation_compression_manual.compress_now
(the CLI, TUI and ACP surfaces now route through it; the gateway slash handler
still passes the literal).

test_compaction_trigger_coverage: the manual-surface guard now pins the label
in the shared core and accepts a surface that routes through compress_now
(cli -> hermes_cli/cli_session_mixin.py, acp -> acp_adapter/commands.py).
test_compress_context_progress_timeout: upstream #114594's exact-equal clamp
numbers replaced by the fork reconcile_timeouts invariants (idle lifted strictly
above the aux deadline, ceiling admits one fallback) already pinned by
tests/gateway/test_compress_abort_honesty.py.
test_length_continuation_thinking_exhaustion: helper imports repointed to
tests.run_agent._run_agent_helpers (upstream moved tests/agent/test_run_agent).

Verified: tests/agent/test_compaction_trigger_coverage.py
tests/agent/test_compress_context_progress_timeout.py
tests/agent/test_length_continuation_thinking_exhaustion.py
tests/gateway/test_compress_abort_honesty.py -> 72 passed, 1 pre-existing
unrelated failure (locale key; fails identically without this diff).

* fix(parity 2026-10-01 ci): reply re-anchor must not bind the engine's kept head to a tail twin

Upstream #118900 (_ensure_compressed_keeps_last_assistant_reply) locates the
folded reply's slot by scanning `compressed` for the LAST row content-equal to
the reply's original follower. When the engine keeps the head verbatim and the
trailing user turn is a content twin of a head row (fork #942 guard:
test_plugin_equal_head_cannot_match_overlapping_tail), that scan bound the kept
HEAD row as the follower and inserted the reply in front of the engine's head
(`[reply, head, notice]`): the notice placement the fork pins was pulled out of
order.

Apply the fork's head-first overlap rule (same as _reinsert_tool_notice_events):
leading `compressed` rows that align positionally with the original rows BEFORE
the reply are the kept head — excluded from the follower scan and never read as
the trailing real user turn (`_kept_head_len`, `kept_head=` on
_reply_insertion_index).

Tests (fork contracts adapted to the adopted upstream mechanisms):
- test_confab_notice_e2e::test_compaction_notice_without_successor_uses_original_boundary:
  the folded middle row now carries real weight, since #118900's reply
  re-anchor plus the user-turn anchor made the one-word candidate GROW and the
  commit-site anti-growth guard refused the whole compaction (fixture artefact).
  Assert the notice's left neighbour (surviving predecessor / summary boundary),
  not a list index.
- test_compression_concurrent_fork::test_compression_restores_user_turn_when_compressor_drops_all_users:
  the restored row now also carries upstream's durable `message_uid` +
  `timestamp` (751d8526e35); compare role/content only (contract: CONTENT survives).

Verified: tests/agent/test_confab_notice_e2e.py +
tests/agent/test_compression_concurrent_fork.py -> 129 passed.
tests/agent/test_confab_matrix_{bigargs_lcm,image_builtin,image_lcm} -> 48 passed;
tests/agent/test_confab_matrix_{merge_builtin,merge_lcm,twins_builtin} -> 48 passed.
tests/agent/test_compression_last_assistant_anchor.py: 6 reds pre-exist on the
lane base (fixture `None is not None` at L117), unchanged by this diff, not in
any round-5 manifest.

* fix(parity 2026-10-01 ci): restore the measured 872K gpt-6 Codex windows and the large-policy picker hint

agent/model_metadata.py: upstream carried the gpt-5.6 900K verdict forward
onto gpt-6-sol/luna (family prefixes) and bumped gpt-6-astra to 900K. The
fork measured all three against the codex-sub catalog
(max_context_window=872,000; astra 2026-09-04, sol/luna 2026-09-22) and
lists them EXACTLY so unprobed gpt-6 descendants never inherit a cap. The
merge took upstream's values -> every fork window test resolved 900,000
instead of 872,000. Fork table + exact-only gpt-6 eligibility restored.

hermes_cli/models_validate.py: the ineligible `-900k` rejection lost the
fork's policy-aware hint (under `large` the bare slug already carries the
window, so the picker example is `gpt-6-sol`, not `gpt-5.6-sol-900k`).

tests/agent/test_codex_context_policy.py (fork-only test, adapted to the
adopted upstream seams, no contract change):
- `patch("agent.model_metadata.requests.get")` -> `model_metadata_http.get`
  (upstream routes every metadata probe through model_metadata_http; the
  lazy `requests` shim is gone).
- `validate_requested_model` imported from hermes_cli.models_validate
  (upstream split hermes_cli.models).
- the live-catalog test hands a JWT-shaped token: upstream #121486 refuses a
  non-JWT credential aimed at chatgpt.com before probing.

Verified: tests/agent/test_codex_context_policy.py -> 64 passed (was 35 red).
tests/hermes_cli/test_model_validation.py: 6 failed + 1 error identical with
and without this diff (Anthropic warning wording / registry container),
not in any round-5 manifest.

* fix(parity 2026-10-01 ci): re-thread per-class skew calibration and idle blackbox bookkeeping onto the turn_* split

agent/turn_context_compaction.py + agent/turn_preflight.py +
agent/turn_request_assembly.py: the fork's P2 "compact on the truth" trigger
(note_rough_sent / calibrated_tokens / should_compress_request /
trigger_compare_tokens_for with `messages` threaded through so each request
is classified per content class; R05 ledger TODO `turn_preflight L3548`) was
dropped when upstream extracted the preflight into turn_context_compaction
and the pre-API gate into turn_preflight. Both gates compared the raw rough
estimate again, so the whole per-class arm was dead code. Restored on the
extracted modules: the prologue preflight uses the surviving
_preflight_request_tokens_split (rough vs anchored), the pre-API gate reads
the rough figure request assembly now stashes beside the anchored flag
(agent._request_pressure_rough). Non-bool verdicts from MagicMock doubles
fall back to should_compress, as on fork/main.

agent/turn_context_compaction.py: idle-resume compaction lost the fork's
blackbox bookkeeping (idle_compaction_fired + before/after tokens, C5 #42);
restored, estimator read through the turn_context facade, failure-tolerant.

agent/chat_completion_helpers.py: _summary_text reads tool_calls via getattr
so a minimal normalized shape (content only) is not a summary failure.

Tests:
- test_per_class_skew_calibration: the two AST guards follow the production
  sites into turn_context_compaction / turn_preflight (module getters only).
- test_iteration_limit_summary_display_fields: upstream routes the chat-mode
  summary through _build_api_kwargs -> _interruptible_api_call (keeps the
  cached prefix); the doubles now sit on those seams, contract unchanged.

Verified: tests/agent/test_per_class_skew_calibration.py 36 passed;
tests/agent/test_idle_compaction_lock_and_guards.py 5 passed;
tests/agent/test_iteration_limit_summary_display_fields.py 3 passed;
test_preflight_compression_gate + test_turn_context_compaction +
test_engine_preflight_wire 7 passed; test_preflight_lock_defer +
test_preflight_compression_cap_e2e + test_native_preflight_estimate 9 passed;
test_confab_notice_e2e + test_confab_matrix_merge_builtin +
test_confab_notice_compaction_stats 81 passed.

* fix(parity 2026-10-01 ci): price image parts under the shared estimator divisor; D-6 thrash fixture on upstream's bounded tail floor

agent/chat_completion_helpers.py: the stale-call estimator routes through the
fork's shared 3.5 chars/token divisor (886877cfbc8), but upstream's image
pricing (#63871/#76411) expressed the learned per-image TOKEN cost as
image_cost*4 chars — under the fork divisor that inflated every image by
4/3.5. Express it in chars under COMPOSITION_CHARS_PER_TOKEN so it
round-trips to image_cost tokens.

tests/agent/test_context_estimator_multimodal.py (upstream-only): the text
contract pins "no image pricing applied" against the fork divisor instead of
upstream's char/4 literal.

tests/agent/test_compaction_failed_summary_donepath.py (fork-only): upstream
fdbcdef9146 bounds the protect_last_n count floor by the tail token budget
(pinned by test_context_compressor::test_message_floor_does_not_unboundedly_override_soft_ceiling),
so the 4-pair huge tail no longer rides the floor past the ceiling and the
request genuinely shrinks (probe: fork/main 16->12 rows / 87K post; merged
16->6 / 41K). The thrash condition is rebuilt from the REQUIRED anchor pair
(last user #10896 + last assistant #29824), which both trees keep whole.
Contract (failed/placeholder summary over threshold counts ineffective on the
real done-site) unchanged; the done-site code itself survived the merge.

Verified: test_context_estimator_multimodal + test_non_stream_stale_timeout +
hermes_cli/test_load_progress -> 19 passed;
test_compaction_failed_summary_donepath -> 1 passed.

* fix(parity 2026-10-01 ci): re-thread the request-body byte budget and the image-lifecycle invariant onto the turn_* split

The fork's serialized request-body ceiling (agent/request_body_budget.py:
byte preflight after request middleware, terminal re-check once execution
middleware replaced the payload, and body_too_large 413 recovery that
remediates retained images once and otherwise fails the turn actionably,
never text compaction) survived the merge as a module nobody called:
upstream's turn_*.py extraction dropped all three loop sites. New sibling
agent/turn_body_budget.py carries them; wired at
turn_api_request.build_api_request (verdict gains action="return"+result,
honoured by _run_api_retry_loop), turn_api_call._perform_api_call (terminal
edge; raises RequestBodyBudgetExceeded) and turn_api_error.handle_api_error
(right after pool recovery, before retry accounting; ApiErrorVerdict now
carries api_messages so a remediated copy reaches the rebuild).

turn_iteration_prep: the fork's once-per-turn "image lifecycle invariant
violated" warning (image parts before the current-turn boundary) restored;
the flag is reset at the turn prologue.

tests/agent/test_413_compression.py (fork tests/run_agent file merged with
upstream's): patch seams for the mid-turn estimators follow the code into
agent.model_metadata (turn_request_assembly / turn_preflight read lazily;
turn_context keeps its own binding); the rough-growth-after-fit preflight
makes the estimate the deciding signal via note_usage_less_response(), since
the merge adopts upstream 0f4587e336f's deferral (pinned by
test_switch_waits_for_new_provider_evidence and the neighbouring
test_rough_over_threshold_waits_one_request_then_real_usage_compresses),
replacing the fork's (rough, real) growth projection. The calibrated-gate
contract is unchanged.

Verified: tests/agent/test_413_compression.py 41 passed (was 7 red);
test_413_image_payload_recovery + test_request_body_budget +
test_turn_api_error_stream_parse 19 passed; test_turn_api_call_interrupt +
test_image_shrink_recovery + test_retry_exhaustion_partial_retention 24 passed.

* docs(parity 2026-10-01 ci): CI5 ledger for lane L1-agent-confab-compaction

20 manifest files / 196 reds -> all green narrowly on the lane head
(final re-proof: 453 passed across the 20 files, 3 per call).

* fix(parity 2026-10-01 ci): L5 fast mode is route-aware at every call site; Opus 5/5.5 in the fast catalog

tests/cli/test_fast_route_capability.py::test_request_enforcement_call_sites_do_not_use_model_only_wrapper
forbids resolve_fast_mode_overrides( / model_supports_fast_mode( outside hermes_cli/models.py.
Upstream's parallel /fast (agent/fast_mode.py auto|cold windows, tui_gateway._fast_tier_applies,
methods_config_set._set_fast, slash_commands_model._handle_fast_command) called the model-only
wrapper; each now asks the fork's resolve_fast_mode_capability (model + provider + api_mode).

- hermes_cli/models.py: resolve_fast_mode_capability(base_url=) — optional live-endpoint gate
  (_fast_mode_route_supported) so a first-party provider id behind a proxy host fails closed
  the way the upstream wrapper did; threaded through ..._for_configured_route.
- hermes_cli/fast_mode_contracts.py: anthropic_fast = (opus-5-5, opus-5, opus-4-8) per the live
  fast-mode docs (same three ids as agent.model_metadata and the pricing rows);
  normalize_fast_model_id folds dotted opus spellings generally, suffixes still rejected.
- tests: catalog assertion updated; test_fast_command proxy branch pins {} (fork
  `{}`-when-tier-set contract, matches the sibling tests in the same class).

Verified: tests/cli/test_fast_route_capability.py tests/hermes_cli/test_fast_command.py
tests/tui_gateway/test_fast_session_scope.py -> 54 passed. tests/agent/test_fast_mode_auto.py
tests/cli/test_fast_mode_overrides.py tests/gateway/test_fast_command.py tests/agent/test_usage_pricing.py
tests/hermes_cli/test_oneshot_reasoning_and_tier.py: same red set before and after (not L5 files).

* fix(parity 2026-10-01 ci): L5 `config set` keeps comments + bak-configset sibling; codex owner clears cooldown on the refreshed row

- hermes_cli/config.py: restore the fork `config set` writer the merge dropped for upstream's
  full-state ruamel replace: _targeted_config_edit (one-scalar splice / block insert, re-parsed)
  -> roundtrip render -> block dump; refuse with a diff when a comment would be dropped unless
  --force; config.yaml.bak-configset-* sibling with the pre-write bytes (t_6da78aab,
  profile-config-keyguard). `unset` keeps upstream's _write_user_config. PyYAML's IndentDumper /
  yaml.compose are gone upstream (hermes_yaml is ruamel): hermes_yaml.compose() added, block
  dump = hermes_yaml.safe_dump. scripts/check_config_yaml_writers.py: OK.
- agent/codex_owner.py: after the quota probe refreshes an expired stored token (#89415) the
  pool row already holds the fresh pair; clear the cooldown on that row, not the stale
  snapshot (which wrote the expired access token back and resolved it).

Verified: tests/hermes_cli/test_config_set_preserves_comments.py test_config_yaml_comment_preservation.py
test_config_set_list_values.py -> 29 passed; test_config_set_coercion.py test_config_set_platforms_redirect.py
-> 20 passed; tests/hermes_cli/test_auth_codex_quota_probe.py test_auth_codex_provider.py -> green;
tests/agent/test_codex_owner_*: unchanged red set (needs L2's hermes_cli/auth.py _auth_lock_path).

* test(parity 2026-10-01 ci): M2-hermes-clia batch 1 — shell-hook coverage, anon auth, codex self-heal

- test_agent_host_shell_hook_coverage: patch discover_mcp_tools where main.py now imports it
  (tools.mcp_tool_discovery; the tools.mcp_tool re-export went with the compat layer in a5bd246865b)
  and map upstream's extracted gateway/run_turn*.py + slash_commands_session.py to the GATEWAY host.
- test_anon_auth_core (upstream-only): a successful _swap_credential returns the fork's
  SwapOutcome.SWAPPED, not bare True.
- test_auth_codex_self_heal #73667 tests (upstream-only): the fork's Codex owner store (#673) serves
  a singleton with an access_token before the singleton read, so exercise the CLI recovery through
  the rejected-refresh path (_refresh_codex_auth_tokens) with the same CAS / workspace assertions.

* fix(parity 2026-10-01 ci): L5 cron list default set, lazy provider catalog, web_server facade symbol

- hermes_cli/cron.py: `cron list` asks the store for include_disabled=show_all (fork --json
  contract) and tops the human table up with paused jobs (upstream 3f399c0bd4 contract).
- hermes_cli/models_catalog_static.py: drop the import-time sync_plugin_provider_catalog():
  list_providers() imports plugins that import hermes_cli.models -> partially initialised
  module (fork lazy auto-extend contract; the post-discovery sync hook still runs it).
- hermes_cli/web_server.py: module-level get_hermes_home facade symbol (tests patch it).
- tests: desktop cron ticker test admits the fork can_dispatch kwarg (#1373);
  gpt-6.1-sol IS -900k eligible on the fork (#1550, measured 922k) - upstream assertion flipped.

Verified: tests/hermes_cli/test_cron_list_json.py test_dashboard_state_db_log.py
test_desktop_cron_ticker_gateway_standdown.py -> 18 passed; tests/hermes_cli/test_cron.py -> 29 passed;
tests/hermes_cli/test_lazy_canonical_providers.py test_gpt6_tiers_registration.py
test_list_picker_providers.py -> 41 passed; test_api_key_providers.py test_provider_groups.py green.

* fix(parity 2026-10-01 ci): M1 — re-thread track_agent persist + wecom final-frame tail; F02c vocabulary on injection test

- gateway/run_turn.py _run_agent_track_agent: restore the fork self._persist_active_agents() after the sentinel->agent promotion (2026-09-19 regression guard); upstream extracted the closure and the merge kept only the upstream body.
- plugins/platforms/wecom/streaming.py _send_stream_reply: final frame keeps head AND tail (t_11223645) — upstream split the adapter, merge took head-only.
- tests: track_agent AST test follows the extraction (run_turn.py, tuple-assign form); injection ack test asserts the F02c True/False/None seam, ended-session drop is owned by test_completion_delivery.

* fix(parity 2026-10-01 ci): M5 lane — ci + lcm smoke reds

- tests/ci/test_evaluate_needs.py: upstream dropped the pyyaml dependency
  (284dbaf5370, YAML unified on ruamel); read ci.yaml via hermes_yaml.
- tests/ci/source_proxy_baseline.json: ratchet re-baselined on the merged
  test set — 48 entries whose tests the merge deleted/rewrote removed,
  the fork's relocated tests/run_agent -> tests/agent fallback-override
  key re-keyed, three upstream-authored pre-existing proxies and the L2
  AST-walk rewrite of test_every_fallback_activation_restarts_preflight
  frozen (pre-existing, not endorsed; count 114 -> 67).
- scripts/probe_hermes_lcm_isolated.py: compare live roots lexically; the
  merged tests/home_io_guard refuses realpath() under the real home, so
  resolving ~/.hermes/plugins to decide a refusal tripped the guard on
  every smoke test. Only the candidate path is resolved now.

Verified: tests/ci/test_evaluate_needs.py + test_no_new_source_proxy_asserts.py
+ tests/context_engine/test_lcm_adoption_smoke.py 32 passed under e45-pt;
smoke 6/6 under a non-temp HERMES_HOME (6/6 fail without the probe change).

* fix(parity 2026-10-01 ci): M1 — yaml shim in slash_commands, completion silence hint on the extracted formatter

- gateway/slash_commands.py: two raw `import yaml` -> `import hermes_yaml as yaml` (CI venv has no PyYAML; the fork reads YAML through hermes_yaml).
- tools/process_registry_notifications.py: upstream extracted format_process_notification and the copy dropped the fork COMPLETION_SILENCE_HINT tail (Ace contract 2026-09-27); constant now lives in the notifications module, re-exported from tools.process_registry.
- tests: yaml shim in two tests; compress locale sweep skips upstream *.tui.yaml catalogs; goal wait_on test stubs upstream _pid_alive; fast_command provider doubles accept target_model=.

* fix(parity 2026-10-01 ci): M5 lane — acp_adapter reds

- agent/conversation_compression_manual.py: compress_now passes
  trigger_reason="manual_compress_command" (same 3-line change as the
  unfolded L1 lane, cb0f5645c50; applied here so the ACP test is green on
  this branch regardless of fold order — identical bytes, folds clean).
- tests/acp_adapter/test_session.py: get_messages_as_conversation(
  include_timestamp=True) — the fork's default projection is byte-stable
  (F01), same adaptation L2 made for test_inline_edit_persistence.

Verified: tests/acp_adapter/test_server.py + test_session.py 59 passed (e45-pt).

* fix(parity 2026-10-01 ci): M2-hermes-clia batch 2 — codex owner row identity, auth refresh outcome, kimi/pool fixtures

- agent/codex_owner._current: compare the hydrated PooledCredential.source, not the raw row column:
  a row written without `source` loads as SOURCE_MANUAL on both sides and is the same generation
  (upstream's profile-shadowing test in test_auth_profile_fallback seeds such rows).
- hermes_cli/auth_commands.auth_refresh_command: the fork's Codex owner store raises AuthError on
  a failed/uncertain refresh instead of returning None; map it to the same "Could not renew" exit.
- test_auth_kimi_oauth_provider: drop the load_pool->None stub; the merged _add_kimi_oauth_credential
  (upstream shape) reads the re-seeded pool entry.
- test_auth_pool_operations (upstream-only): rows start unbenched (the owner store refuses a forced
  refresh of a cooldown row) and a non-429 failure is asserted as the owner's receipt fence
  (dead / codex_refresh_uncertain, pair untouched) rather than upstream's exhausted/dead split.

* fix(parity 2026-10-01 ci): restore the confab tool-call notice recovery on the extracted turn loop

The 2026-10-01 merge kept upstream's extracted agent/turn_*.py siblings and dropped three fork
seams (#942, agent/confab_notice.py) that lived inline in conversation_loop.run_conversation:

- turn_response_intake.normalize_model_response: announce a validated out-of-band notice once
  per turn (should_announce_notice ledger) and carry _confab_notice/_new_confab_notice on the
  verdict (+ _LoopState slots) for the final-text phase.
- turn_final_response.finish_text_response: tool_call_as_text / tool_call_unparseable notices
  re-prompt with the fixed TOOL_CALL_NOTICE_TEXT instruction BEFORE the empty-response ladder,
  persist the metadata-only system event row, share the 3-stall dropped-tool-call budget, and
  end the turn failed (tool_call_recovery_exhausted) when it is spent. Without this the merged
  loop fell into the empty-response retry (the StopIteration lead signature: one extra provider
  call per scripted notice across 137 reds).
- turn_context.build_api_messages: metadata-only notice rows never reach the provider request.

Fixture/assert updates in tests/agent/test_confab_notice_e2e.py for upstream mechanisms the
merge legitimately adopted (evidence in the CI5 ledger): 0f4587e336f first-request preflight
deferral (arm the built-in compressor with a real-usage anchor; the mock never prices the
transcript), #118900 last-assistant-reply re-anchor (pin the notice's neighbour, not list
length), 8f0322da5b8 failed_turn boundary row (exclude the Hermes-authored row).

Verified: tests/agent/test_confab_notice_compaction_stats.py 1/1; test_confab_notice_e2e.py
lcm shards + valid_empty_tool_event_is_not_replayed green after the turn_context port; full
file re-proof in progress.

(cherry picked from commit 0813ef705ecbfb71cf939685ee1dee725dd0ad19)

* fix(parity 2026-10-01 ci): M4-tools — approval bypass facade, async-delegation retirement/prune guards, formatter re-export

- tools/approval.py: every bypass site goes through is_approval_bypass_active() again (the merge
  re-introduced a hand-rolled `_yolo_active()` that dropped approvals.mode=off — the fork 2026-09-08
  incident); `_get_approval_mode` is a facade delegate so tests can patch either module.
- tools/async_delegation.py: `_dispatch_admitted` tolerates an executor double returning no future
  (fork test doubles) instead of rejecting as submission_failed; `_prune_completed_locked` no longer
  hashes a runner-supplied status (fork hostile-__hash__ guard).
- tools/process_registry.py: re-export `_format_async_delegation` (fork facade symbol upstream moved).
- tests/tools/test_async_delegation_terminal_receipts.py: dispatch helper assertion carries the payload.

Verified narrowly via scripts/test-gate: test_approval_mode_off_bypass + approval_mode_parity + test_approval
174 passed; numeric_binding_boundary + registry_boundaries + terminal_receipts 204 passed.

* fix(parity 2026-10-01 ci): M1 — discord native slash Range fallback + omitted-option defaults; reasoning descriptions name max/ultra

- plugins/platforms/discord/adapter.py _slash_proxy: resolve app_commands.Range tolerantly (test stubs lack it; bare type keeps the option) and render omitted optional options from the synthesised signature defaults (/undo with no count -> "/undo 1", the fork t_b4f07acf contract). Clears 14 reds in test_discord_slash_commands + 15 connect/liveness/event_silence reds that failed at connect() on the same AttributeError.
- locales/en.yaml platform.discord.command.reasoning: description/arg_effort name the effort levels incl. max + ultra (fork contract; the i18n key form dropped them). gateway/relay/command_manifest.py carries a hardcoded copy — out of lane, FOLLOWUP.
- tests: free_response offload class adapted to the fork sync mark_many + CoalescingJsonWriter seam (fork design; upstream-only tests); producer_matrix seeders import from cron.scheduler_delivery (upstream extraction), capture double stamps the admit_internal_event receipt, F02c True vocabulary.

* fix(parity 2026-10-01 ci): M3-agentb round 1 — re-thread stop-gate, placeholder seam, preflight announce; repoint moved patch targets

- agent/turn_stop_gates.py: kanban stop guard threads tools= into
  build_kanban_stop_nudge; persists the candidate answer (interim flush) and
  flags only the nudge synthetic (t_4eeb0202).
- agent/turn_final_response.py: classify_placeholder_final_text wired at the
  final-text seam before the empty ladder (t_887f9584); streamed buffer cleared.
- agent/turn_context_compaction.py: engine preflight maintenance announce with
  reason template + preflight_is_user_visible gate (fork 5636a8a6d5/829d4e0f3e).
- tests: iteration-limit summary drives agent._interruptible_api_call (summary
  now goes through _build_api_kwargs); model_metadata patches
  model_metadata_http.get/.stream, gpt-6 sol/luna 900K cap w/ catalog max,
  bedrock cache row via bedrock_confirmed provenance; kanban_stop source grep ->
  turn_stop_gates + complete_task result=; _FALLBACK_ANNOUNCE_LABELS rename.

Verified: scripts/test-gate narrow runs — test_iteration_limit_summary_session_user 3 passed,
test_kanban_stop 54 passed, test_model_metadata 158 passed,
test_placeholder_final_text_backstop 17 passed, test_preflight_announce_visibility +
test_pool_exhaustion_scope_label 22 passed.

* fix(parity 2026-10-01 ci): M1-gatewayb batch 1 - hermes_yaml shim, refused-followup report at adapter drop

- gateway/slash_commands.py: two lazy `import yaml` sites -> `import hermes_yaml as yaml`
  (upstream dropped pyyaml from deps; the fork shim is the loader everywhere else).
- tests: test_switch_announce, test_turn_concurrency, test_stop_during_preflight_refuses_turn
  import hermes_yaml (safe_dump, no `dump`).
- gateway/platforms/base.py::_drop_unresolved: a replayed restart follow-up refused by the
  adapter-level profile-route gate (which runs BEFORE the runner ingress gate upstream added)
  now logs PHASE=restart_followup_lost; the log body moved to
  fork_ext/restart_followups.report_refused_followup and run.py delegates to it.

Verified: test_switch_announce+test_stop_during_preflight+test_turn_concurrency 75 passed
(1 local-only home_io_guard false positive under the lane runner, CI-shaped run green);
test_restart_followups_admission_e2e 18 passed.

* fix(parity 2026-10-01 ci): M2-hermes-clia batch 3 — backup disk-image/usage-ledger excludes, CLI receipt batch

- hermes_cli/backup.py: restore the fork's staging disk-image suffix exclusion (.sparseimage /
  .sparsebundle / .dmg; 2026-08-09 42 GB subvps-staging.sparseimage) onto upstream's suffix tuple,
  and keep cache/claude-usage (usage.ace ledger) in the kept cache subdirs.
- test_backup: the root cache/*.MOVED.txt breadcrumb now falls under upstream's regenerable-cache
  rule (same verdict as cache/model_catalog.json); kept-dir assertion carried.
- test_cli_async_delegation_delivery (fork test relocated by upstream's tests/ mirror): completions
  arrive wrapped in ProcessNotificationBatch; unwrap before asserting the accepted input + sibling.

* fix(parity 2026-10-01 ci): M4-tools — HERMES_ALLOW_REBOOT downgrade, sandbox session-id bridge, lifecycle refusal markers

- tools/approval_detection.py: restore the fork HERMES_ALLOW_REBOOT opt-in (reboot/shutdown family
  downgrades from hardline to the DANGEROUS layer; every other hardline pattern untouched). Dropped when
  upstream extracted detection out of tools/approval.py.
- tools/code_execution_env.py: re-thread the fork `_inject_session_id` tail of `_scrub_child_env`
  (#636/C3: contextvar-resolved HERMES_SESSION_ID into the sandbox child; removed when unresolvable).
  Resolver stays on the facade (tools.code_execution_tool._resolved_session_id).
- tools/code_execution_tool.py: re-export `_scrub_child_env` / `_HERMES_CHILD_ALLOWED` (fork facade symbols).
- tools/terminal_tool_guards.py: every gateway-lifecycle refusal carries `blocked_by` =
  GATEWAY_LIFECYCLE_BLOCK_MARKER again (fork: execute_code surfaces blocks instead of a silent 0-exit).
  `_blocked_json` gains an optional `blocked_by`.
- tests/tools/test_execute_code_surfaces_blocks.py: source scan repointed to the extracted
  gateway_lifecycle_block (counts `_blocked_json(` vs markers). RED-proofed: unstamping one refusal fails it.

Verified via scripts/test-gate: test_hardline_blocklist 263 passed; test_execute_code_session_provenance +
test_execute_code_surfaces_blocks 18+11 passed.

* fix(parity 2026-10-01 ci): agent_init tool loader honors both patch contracts (run_agent.* and model_tools.*)

The L7 fold made _load_tools read get_tool_definitions/check_toolset_requirements
through the run_agent facade so fork tests patching run_agent.* work, which
shadowed the 142 upstream-style tests patching model_tools.* (a real catalog
loaded under the mock -> 'clarify' first instead of 'terminal'/'web_search').
_tool_catalog_fn prefers a patched facade attribute, else reads model_tools.

test_primary_runtime_restore: FailoverReason import from agent.error_classifier
(run_agent no longer re-exports it upstream; sole test importer).

Verified: test_run_agent_codex_responses + test_provider_parity +
test_primary_runtime_restore 140 passed; tests/cron/test_cron_fallback_alert_e2e
(run_agent.* patch convention) still green.

* fix(parity 2026-10-01 ci): M3-agenta — compaction attribution, codex ctx facade, credential-pool + anchor seams

agent/model_metadata.py: restore the fork's _resolve_codex_oauth_context_length
compatibility wrapper (upstream kept only the _with_source form; fork callers
and tests resolve through the bare name).

Compaction trigger attribution (cherry-pick of L1 cb0f5645c50 onto the fold
head, applies clean): trigger_reason labels re-threaded onto the extracted
turn_preflight / turn_context_compaction / turn_overflow / turn_recovery /
conversation_compression_manual / gateway run + run_turn call sites. Also
cherry-picked L1 0813ef705ec (confab tool-call notice recovery on the
extracted loop; one trivial union in turn_context.build_api_messages with the
fold's interrupt-close omission) — it clears the 32 confab-matrix
StopIteration reds in this manifest.

Tests adapted to seams the merge legitimately adopted (evidence in the CI6
ledger): compaction lint/announce follow the call sites into the turn_*.py
modules and the CompressionFacadeMixin forwarder; Platform is no longer a
prompt-identity field (#104414, agent/surface_switch.py); the feasibility
probe reuses the main window on a shared route (#89500) so the compressor
double carries context_length/threshold_tokens; model_metadata.requests ->
model_metadata_http.get; xai auth-store sync lives on the consolidated
_sync_entry_from_auth_store; Nous forced refresh adopts a peer-rotated usable
key without redeeming the grant (a6f75130386) while the fork's #670 stale
refusal still redeems the pool token; the fork gates timestamp behind
include_timestamp (#107) and rolls back compactions that INTRODUCE duplicate
active tool results (t_aace5343) — the anchor fixture opts in and persists
the tool rounds the way the loop's per-round flush does. The two real-lcm.db
replay oracles are marked allow_real_home_io (read-only, skip when absent).

Verified narrowly via scripts/test-gate (<=3 files/call):
  codex_subscription_proxy_context + gpt61_sol_900k + gpt61_sol_prestage: 31 passed
  compaction_attribution_lint + compaction_fallback_prompt_identity +
    compaction_stats_reconcile: 112 passed, 2 skipped
  credential_pool_singleton_freshness: 30 passed
  compression_last_assistant_anchor: 8 passed

* fix(parity 2026-10-01 ci): M5 lane — cron reds (11 files)

Code:
- cron/scheduler.py _resolve_job_fallback_chain: a job pinned by MODEL or
  ENDPOINT alone (no provider) never borrows the global chain (upstream
  #100437 / scoped_fallback_chain); the fork's same-provider filter still
  governs provider-pinned jobs and job-declared chains; opt-in / revert env
  var unchanged (shared helper _pin_filter_bypassed).
- cron/scheduler.py _upsert_incident_for_failure(escalation=): the fork's
  one-time stuck page (t_04822736, 3rd identical no_agent failure) is an
  escalation; upstream's failure_repeat_alert_hours cooldown no longer
  swallows it (operator ack still does). Both call sites pass it.
- cron/scheduler.py: the fork's transient-failure page suppression skips
  agent-declared [CRON_FAILURE] evidence (it is the agent's diagnosis, not a
  provider blip; the test pins it verbatim).

Tests (merged contracts):
- test_init_fallback_job_chain: patch targets moved with upstream's split
  (cron.scheduler_delivery._resolve_origin, tools.mcp_tool_discovery).
- test_parallel_pool: mark_execution_running stub returns a row ({}),
  None now means lost ownership (upstream).
- test_oneshot_restart_catchup: the tick's claim_job_for_fire between scans
  — an offered-but-unclaimed slot is restored by design (#107485).
- test_cron_script_job_timeout: monitor.py reads _run_job_script from
  cron.scheduler_script; patch there.
- test_cron_shared_scripts_and_stuck_page: tick 2 sits in the reminder
  cooldown under the merged default (counts 3 -> 2 / 4 -> 3).
- test_lifecycle_guard_heredoc_walk (upstream-only): fork rule
  pc-fb1bd018 — a non-executable oversized file at command position is data;
  the oversized file is chmod +x so the walk is still proven.
- test_cron_no_agent / test_cron_transient_failure_suppression /
  test_cron_script_exit_house_page: wording from the merged copy table
  (cron/scheduler_failure_copy, pinned by test_cron_failure_notice_copy).

Verified (e45-pt, hermetic): all 11 manifest cron files + test_job_fallback_chain
+ test_cron_incidents green: 34 + 48 + 52 + 35 + 18 + 9 passed.

* fix(parity 2026-10-01 ci): M2-hermes-clia batch 4 — fallback-runtime telemetry, resize probe seam, goal barrier timer

- hermes_cli/cli_agent_setup_mixin._resolve_fallback_runtime: the fork's kanban route-ledger calls
  read requested_provider/model via getattr and never veto a resolved fallback (upstream's tests
  build t…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fleetreview:post-merge Ask FleetReview to review this MERGED pull (merge commit vs first parent)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants