From 7eda5a69cc83bae2b78eaac8e37057f9a3180f0f Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Wed, 23 Sep 2026 17:07:15 -0700 Subject: [PATCH] fix(update): a killed custom-branch merge is restored too, from every console script On a custom branch the updater runs `git merge --no-edit origin/` inside the marker window. Its files are the merge of both sides, a blob that is neither pre nor target, so the restore took them for user edits: it put the upstream-only files back to pre, kept the merged ones and spent the marker, leaving a mixed tree (a real random-kill of that merge: 104 of 500 trials broken). - _early_recovery: when pre and target diverge, `git merge-tree --write-tree pre target` gives the tree the merge was writing; its blobs (and prefixes of them, for a file cut short) count as git's like the target's. Conflicted paths, and on git < 2.38 every path both sides changed, count as git's whatever their content. Paths with a newline are hashed one by one (`--stdin-paths` is newline-delimited). The docstring lists the by-design limits. - run_agent (`hermes-agent`) and acp_adapter.entry (`hermes-acp`) never import hermes_cli.main, so they now run the same restore right after hermes_bootstrap (run_agent only when hermes_cli.main is not loaded, since it is also a library module). - Tests: the second test kills inside a clean custom-branch merge (merged file, upstream-only file, a cut-short file, a user edit); the first pins that each console script's entry module imports no other checkout module before the restore runs. Both red on the previous head. - evals/update_pipeline/interrupted_pull_ab.sh gains scenario E: a kill inside the custom-branch merge, then the `hermes-agent` import. --- acp_adapter/entry.py | 7 ++ evals/update_pipeline/interrupted_pull_ab.sh | 42 ++++++- hermes_cli/_early_recovery.py | 106 ++++++++++++------ run_agent.py | 11 +- .../test_update_interrupted_pull.py | 53 ++++++++- 5 files changed, 177 insertions(+), 42 deletions(-) diff --git a/acp_adapter/entry.py b/acp_adapter/entry.py index 0851ad1676..fc3386f034 100644 --- a/acp_adapter/entry.py +++ b/acp_adapter/entry.py @@ -20,6 +20,13 @@ else: # Stop a ``utils/``/``proxy/``/``ui/`` package in the launch cwd from shadowing Hermes modules. hermes_bootstrap.harden_import_path() +# `hermes-acp` runs without hermes_cli.main: repair a `hermes update` killed mid-pull here, before +# importing anything else from the checkout (a no-op under `hermes acp`, which already did). +from hermes_cli import _early_recovery + +if _early_recovery.restore_interrupted_pull(): + _early_recovery.relaunch_after_restore() + import argparse import asyncio import logging diff --git a/evals/update_pipeline/interrupted_pull_ab.sh b/evals/update_pipeline/interrupted_pull_ab.sh index 87c6b937af..c923763265 100755 --- a/evals/update_pipeline/interrupted_pull_ab.sh +++ b/evals/update_pipeline/interrupted_pull_ab.sh @@ -4,14 +4,15 @@ # evals/update_pipeline/interrupted_pull_ab.sh