{"repo":"NousResearch/hermes-agent","public_id":"26172","display_label":"Fork upstream sync in hermes update command","summary":"These issues and PRs address a bug where `hermes update` incorrectly reports 'Already up to date' on forks when the upstream remote is ahead of origin. Multiple fix attempts focus on consulting upstream before returning early.","subsystem":"hermes_cli","counts":{"members":4,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":1,"merged_prs":1,"attempt_prs":2},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":33452,"merged_at":"2026-05-27T20:10:51+00:00"},"resolution":{"state":"resolved","by":"pr-33452"},"freshness":{"state":"verified","last_verified_at":"2026-05-27T20:10:51+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_SPANNING","link_density":0.5},"fix_edges":[{"issue_number":26172,"pr_number":33452,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true,"pr_merged_at":"2026-05-27T20:10:51+00:00"}],"members":[{"kind":"issue","number":26172,"title":"`hermes update` on forks reports \"Already up to date!\" when origin/main matches HEAD but upstream/main is ahead","url":null,"state":"closed"},{"kind":"pr","number":26249,"title":"fix(update): consult upstream before early return","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"6f2a2f157","number":0},{"kind":"commit","value":"53bdef577","number":0},{"kind":"pr","value":"#33452","number":33452},{"kind":"pr","value":"#4873","number":4873},{"kind":"pr","value":"#26172","number":26172}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":29524,"title":"fix(update): sync fork from upstream before fetch/pull","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#33452","number":33452},{"kind":"pr","value":"#4873","number":4873},{"kind":"pr","value":"#26172","number":26172}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":33452,"title":"fix(update): check upstream even when origin/main has no new commits (salvages #4873)","url":null,"state":"merged","close_reason":{"category":"merged"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/main.py","tests/hermes_cli/test_cmd_update.py"],"fix_hunks":[{"file":"hermes_cli/main.py","hunks":["@@ -8928,6 +8928,11 @@ def _cmd_update_impl(args, gateway_mode: bool):\n \n         if commit_count == 0:\n             _invalidate_update_cache()\n+\n+            # Even if origin is up to date, the fork may be behind upstream\n+            if is_fork and branch == \"main\":\n+                _sync_with_upstream_if_needed(git_cmd, PROJECT_ROOT)\n+\n             # Restore stash and switch back to original branch if we moved\n             if auto_stash_ref is not None:\n                 _restore_stashed_changes("]},{"file":"tests/hermes_cli/test_cmd_update.py","hunks":["@@ -106,6 +106,33 @@ def test_update_already_up_to_date(\n         pull_cmds = [c for c in commands if \"pull\" in c]\n         assert len(pull_cmds) == 0\n \n+    @patch(\"shutil.which\", return_value=None)\n+    @patch(\"subprocess.run\")\n+    def test_update_on_fork_checks_upstream_when_origin_up_to_date(\n+        self, mock_run, _mock_which, mock_args, capsys\n+    ):\n+        \"\"\"Regression for issue #26172: forks whose local HEAD already matches\n+        origin/main must still consult upstream/main before printing\n+        \"Already up to date!\" — otherwise a fork that's caught up to its own\n+        origin but behind NousResearch/hermes-agent silently misses updates.\n+        \"\"\"\n+        from hermes_cli import main as hm\n+\n+        mock_run.side_effect = _make_run_side_effect(\n+            branch=\"main\", verify_ok=True, commit_count=\"0\"\n+        )\n+\n+        with patch.object(\n+            hm,\n+            \"_get_origin_url\",\n+            return_value=\"https://github.com/example/hermes-agent.git\",\n+        ), patch.object(hm, \"_sync_with_upstream_if_needed\") as sync_mock:\n+            cmd_update(mock_args)\n+\n+        sync_mock.assert_called_once_with([\"git\"], PROJECT_ROOT)\n+        captured = capsys.readouterr()\n+        assert \"Already up to date!\" in captured.out\n+\n     @patch(\"shutil.which\")\n     @patch(\"subprocess.run\")\n     def test_update_refreshes_repo_and_tui_node_dependencies(\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}