Repository navigation
fix(lifecycle-guard): gate the read-only Python path mask on a whole-body allowlist - #1017
Conversation
…body allowlist The mask trusted a body unless it matched a deny-list of rebinding shapes. A trusted object can be changed through a Load-context call that binds no name, which a deny-list cannot see. The mask now applies only when every node, import, name and call in the body is in an enumerated safe set; anything else keeps the conservative shell-reference scan.
|
🤖 merged-by: apollo · lane: kanban-merge-pass · gate: BYPASS: FleetReview paused by Ace 2026-09-22 (state/fleetreview-pause marker present) · why: t_594a24a2: lifecycle_guard: replace _mask_read_only_python_paths deny-list with a whole-bod; Argus off card review (Ace 13:08), CI green |
|
🤖 merged-by: apollo · lane: discord · gate: BYPASS: FR PAUSED by Ace 2026-09-22; Argus removed by Ace 2026-09-24 13:08; gate = handoff + CI green + Apollo read (Ace 15:1x: handle all cards) · why: t_594a24a2: fix(lifecycle-guard): gate the read-only Python path mask on a whole-body allowlist (+202/-34, 2 files) — handoff + CI green + Apollo read |
|
🤖 merged-by: apollo · lane: kanban-merge-pass · gate: BYPASS: FleetReview paused by Ace 2026-09-22 (state/fleetreview-pause marker present) · why: t_594a24a2: lifecycle_guard: replace _mask_read_only_python_paths deny-list with a whole-bod; worker completed in place, CI green |
FleetReviewReview: post-merge · head Post-merge review ( Reviewed with 2 of 3 model families — xai unavailable. profile: light (rule: default light: lines 236<800, files 2<1000000, hunks 3<1000000, no hot path) · round 0 · members: L6, C-assert-xhigh, F, G · families: anthropic,openai Confidence: 3/5 Findings
FleetReview provenance · models: B=gpt-6-sol, C=claude-code-opus-5-5, D=grok-4.6, F=gpt-6-sol · cost: $1.82 · duration: 12m 19s · rounds: 2 · files examined: 2 |
…s (t_9c9e757c) FleetReview retro-backfill 2026-09-27, class C4. Each finding re-verified at 5a9d284; one RED-on-base / GREEN test per confirmed instance. - #813 agent/quota_registry_gate.py: missing/unparseable observed_at now fails open (docstring contract), no longer skips a working fallback. - #1017 cron/lifecycle_guard.py: mask allowlist refuses module callables passed as values (json.dumps(p, default=subprocess.run), sorted(key=...)). - #1019 agent/lsp/manager.py: slot-dir OSError in _has_capacity -> run without LSP instead of raising out of enabled_for()/write_file. - #1026 cron/scheduler.py: owner-deadman exemption scoped to the host it owns; a note naming only another DOWN host is gated. - #1229 scripts/ci/live_comment.py: comment lookup/update network errors no longer kill the poller; a failed post is retried next poll. - #1230 plugins_cmd.py: pinned-plugin remedy shell-quotes the recorded source (CLI + dashboard). - #1238 tools/mixture_of_agents_tool.py: reference responses move from the aggregator system message to a tagged data block in the user message. Verified: test-gate narrow pytest; new tests 13 RED on base, all GREEN with fix; neighbouring suites (lifecycle_guard x4, host_down_gate, quota wiring, plugins_cmd, fallback_policy, fork toolsets) pass.
…s (t_9c9e757c) FleetReview retro-backfill 2026-09-27, class C4. Each finding re-verified at 5a9d284; one RED-on-base / GREEN test per confirmed instance. - #813 agent/quota_registry_gate.py: missing/unparseable observed_at now fails open (docstring contract), no longer skips a working fallback. - #1017 cron/lifecycle_guard.py: superseded by #1348 (6662bbd) on main; this slice's 5 callback cases pass against it, so the edit was dropped on rebase. - #1019 agent/lsp/manager.py: slot-dir OSError in _has_capacity -> run without LSP instead of raising out of enabled_for()/write_file. - #1026 cron/scheduler.py: owner-deadman exemption scoped to the host it owns; a note naming only another DOWN host is gated. - #1229 scripts/ci/live_comment.py: comment lookup/update network errors no longer kill the poller; a failed post is retried next poll. - #1230 plugins_cmd.py: pinned-plugin remedy shell-quotes the recorded source (CLI + dashboard). - #1238 tools/mixture_of_agents_tool.py: reference responses move from the aggregator system message to a tagged data block in the user message. Verified: test-gate narrow pytest; new tests 13 RED on base, all GREEN with fix; neighbouring suites (lifecycle_guard x4, host_down_gate, quota wiring, plugins_cmd, fallback_policy, fork toolsets) pass.
…s (t_9c9e757c) (#1353) FleetReview retro-backfill 2026-09-27, class C4. Each finding re-verified at 5a9d284; one RED-on-base / GREEN test per confirmed instance. - #813 agent/quota_registry_gate.py: missing/unparseable observed_at now fails open (docstring contract), no longer skips a working fallback. - #1017 cron/lifecycle_guard.py: superseded by #1348 (6662bbd) on main; this slice's 5 callback cases pass against it, so the edit was dropped on rebase. - #1019 agent/lsp/manager.py: slot-dir OSError in _has_capacity -> run without LSP instead of raising out of enabled_for()/write_file. - #1026 cron/scheduler.py: owner-deadman exemption scoped to the host it owns; a note naming only another DOWN host is gated. - #1229 scripts/ci/live_comment.py: comment lookup/update network errors no longer kill the poller; a failed post is retried next poll. - #1230 plugins_cmd.py: pinned-plugin remedy shell-quotes the recorded source (CLI + dashboard). - #1238 tools/mixture_of_agents_tool.py: reference responses move from the aggregator system message to a tagged data block in the user message. Verified: test-gate narrow pytest; new tests 13 RED on base, all GREEN with fix; neighbouring suites (lifecycle_guard x4, host_down_gate, quota wiring, plugins_cmd, fallback_policy, fork toolsets) pass. Co-authored-by: ang-fleet-workers[bot] <333956806+ang-fleet-workers[bot]@users.noreply.github.com>
…er 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 (NousResearch#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 (NousResearch#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).
…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…
… 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…
Card t_594a24a2 (derived from t_9978e3f6 review round 5).
_mask_read_only_python_pathstrusted a heredoc body unless it matched a deny-list of rebinding shapes. A trusted object can be changed in place through a Load-context call that binds no name, which no deny-list of binding shapes can see. The mask now applies only when every node, import, name and call in the body is in an enumerated safe set (_python_body_is_mask_safe). Anything else keeps the conservative shell-reference scan.Verification (local, repo venv):
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.