On Windows, subprocess text=True without an explicit encoding decodes
child output with the ANSI code page (e.g. 'gbk'); non-ASCII bytes then
raise UnicodeDecodeError inside subprocess._readerthread, killing the
Hermes backend before it becomes ready and surfacing as the desktop boot
timeout.
Sweep every hermes_cli text=True subprocess call to encoding='utf-8',
errors='replace', and add an AST-based regression test that fails when a
future text-mode call omits the encoding.
Fixes#55658
_git_origin_url spawned a bare 'git' while every other plugin git call
goes through _resolve_git_executable(); from a service with a minimal
PATH (or Windows without Git on PATH) the probe failed where install
and update succeed. No git at all now goes straight to the .git/config
parse.
Task 1 of the plugin auto-update plan (settled 2026-09-03,
.hermes/plans/2026-09-03_120000-plugin-auto-update-system.md):
- hermes_cli/plugins_provenance.py: the 2x2 classifier — row-presence
says 'hermes installed this' (.install-metadata.json sidecar),
.git-presence cross-checks it: row+git=git install, neither=manual
drop, git-only=self-cloned (origin URL carried ready for adoption,
real-git then .git/config-parse fallback), row-only=drift (flagged).
Pure functions, fully unit-tested truth table.
- cmd_adopt: self-cloned dirs become tracked git installs (origin URL
validated, revision recorded, row written). Refuses non-self-cloned
and already-rowed plugins.
- cmd_trust_update_url: the ONLY path that moves a saved update_url
tag — confirms a manifest's changed url into the sidecar after
review; refuses when there's nothing to trust. The needs-fixing
mismatch remedy from Task 2.
- _install_plugin_core: copies the manifest's update_url into the
sidecar row at install — the SAVED TAG (claims vs provenance).
- plugins list: provenance class shown for non-git user plugins.
- parser + dispatch: 'adopt' and 'trust-update-url' subcommands.
tests: 6 provenance-truth-table tests; existing plugins suite green
(80 passed across both files).