Termux interrupted-update recovery loop: wrong extras group + no reset path (related to #39106), plus unrelated Python 3.14 ThreadPoolExecutor incompatibility in daemon_pool.py
type/bugcomp/cliP2needs-reprosweeper:risk-compatibilityarea/install-update
## Section 1: Termux update/recovery extras-group bug
Cross-reference existing issue #39106, which documents `cmd_update` on Termux defaulting to `.[all]` instead of `.[termux-all]`, causing `cryptography` wheel-build failures (`cryptography-50.0.0-cp314-abi3-android_28_arm64_v8a.whl is not compatible with the current Python 3.14 on Android`).
Report that the same bug also exists in the interrupted-update recovery path in `hermes_cli/_early_recovery.py` (near the "Pending interrupted-update install has already failed 3 times" message), which fires on *every* command (`hermes status`, `hermes doctor`, etc.) whenever `.update-incomplete` exists — broader impact than #39106 describes. We did not capture exact line numbers; ask maintainers to check that file's dependency-install call for the same missing `_is_termux_env()` detection that `cmd_setup` already has.
Also report: there's no supported way to clear a stuck `.update-incomplete` marker once resolved manually — it's only cleared by `cmd_update`'s own internal success path. Suggest a `hermes update --reset-state` command that clears it after confirming the environment imports cleanly.
## Section 2: Python 3.14 ThreadPoolExecutor incompatibility in daemon_pool.py
`tools/daemon_pool.py`'s `DaemonThreadPoolExecutor._adjust_thread_count()` calls the internal `concurrent.futures.thread._worker()` function using the old signature and argument order:
```python
args=(
weakref.ref(self, weakref_cb),
self._work_queue,
self._initializer,
self._initargs,
)
```
Python 3.14 changed `_worker()`'s signature to `(executor_reference, ctx, work_queue)`, replacing `initializer`/`initargs` with a `WorkerContext` object produced by `self._create_worker_context()` (set up in `ThreadPoolExecutor.__init__` via `type(self).prepare_context(initializer, initargs, **ctxkwargs)`).
The old code fails immediately under Python 3.14 with:
```text
TypeError: _worker() takes 3 positional arguments but 4 were given
```
An intermediate fix attempt using `self._thread_context` also fails, since that attribute doesn't exist in 3.14:
```text
AttributeError: 'DaemonThreadPoolExecutor' object has no attribute '_thread_context'
```
The correct fix, verified working (test passed: `HERMES_EXECUTOR_OK`):
```python
def _adjust_thread_count(self) -> None:
if self._idle_semaphore.acquire(timeout=0):
return
def weakref_cb(_, q=self._work_queue):
q.put(None)
num_threads = len(self._threads)
if num_threads < self._max_workers:
thread_name = "%s_%d" % (self._thread_name_prefix or self, num_threads)
t = threading.Thread(
name=thread_name,
target=_worker,
args=(
weakref.ref(self, weakref_cb),
self._create_worker_context(),
self._work_queue,
),
daemon=True,
)
t.start()
self._threads.add(t)
```
Note this deliberately preserves Hermes' original behavior of not registering the thread in `_threads_queues`, keeping it a true daemon thread.
Ask whether this is already tracked elsewhere before filing as new, since it may affect any Hermes install on Python 3.14+ regardless of platform, not just Termux.
## Environment for both
- Hermes Agent 0.20.3
- Termux on Android
- aarch64 / Samsung device
- Python 3.14.6
## Verification notes
- The `.[termux-all]` fix, marker removal, and `daemon_pool.py` patch were all verified working locally.
- `hermes update` itself was not re-tested after the fix — describe it as "avoided," not "confirmed fixed."
0 条评论