Skip to content

perf(cli): global --version fast path via canonical _startup_fast module (#37704 salvage) - #62096

Merged
kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/37704-fast-version
Aug 2, 2026
Merged

perf(cli): global --version fast path via canonical _startup_fast module (#37704 salvage)#62096
kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/37704-fast-version

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Summary

hermes --version answers in ~0.01s on every platform (was ~3.8s cold / 0.2-0.4s warm) — the Termux-only pre-import fast path is now global, rebuilt around a single canonical module so the fast path and the real CLI can no longer drift apart.

Salvages #37704 by @hunjaiboy (cherry-picked, authorship preserved) + two follow-ups, including a fix for a live bug on main: the existing Termux fast path NameErrors on --version today (_print_fast_version_info references PROJECT_ROOT, which is defined after the fast exit point — broken by eb40402, reproduced on pinned main with TERMUX_VERSION=0.118).

Changes

  • hermes_cli/main.py (perf(cli): fast-path global version startup #37704, hunjaiboy): extend the ultrafast --version exit to all platforms, with a conservative container-mode probe (any ambiguity → slow path, which owns authoritative container routing).
  • hermes_cli/_startup_fast.py (follow-up): THE canonical stdlib-only implementations — project-root resolution, Termux/container/profile probes, version printing. main.py's *_fast() names become thin delegates and PROJECT_ROOT derives from the same helper, killing the duplicated-heuristic bug class structurally. Fast output additionally reads the .install_method stamp and points at hermes version for update status (nothing silently dropped vs the slow path; the version subcommand keeps full output).
  • tests/hermes_cli/test_startup_fast_guards.py: guard tests preventing regression of the class — (1) subprocess import-weight check: no heavy module (config/yaml/argparse/cli/run_agent/httpx/openai) may load with _startup_fast; (2) subprocess output parity on+off Termux — the test that would have caught eb40402 the day it landed; (3) install-method stamp surfacing.

Validation

Before After
hermes --version (all platforms) 3.8s cold / 0.2-0.4s warm 0.01s
hermes --version on Termux (main today) NameError traceback works
hermes version subcommand full output + update check unchanged (slow path)
NixOS container mode routed into container unchanged (probe bails to slow path — verified with a real .container-mode file)

59 targeted tests green (incl. 4 new guards); script-mode (python hermes_cli/main.py --version) verified; ruff clean; ty 46==46 vs base. Known pre-existing: test_update_falls_back_to_main_when_branch_not_on_remote fails when sharing an interpreter with test_tui_resume_flow — reproduces identically on pinned upstream/main, invisible under the canonical per-file runner.

Supersedes #37704 — can be closed in favor of this.

@alt-glitch alt-glitch added type/perf Performance improvement or optimization comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have labels Jul 10, 2026
@jimmyjonezz

Copy link
Copy Markdown

Confirmed: this bug reproduces on Termux (Android) + Python 3.14.6.

Steps:

  • Fresh checkout of main (e598cef)
  • Run hermes version or hermes --version
  • Result: NameError: name 'PROJECT_ROOT' is not defined at hermes_cli/main.py:248

Root cause (as described in the PR): _print_fast_version_info() references PROJECT_ROOT before it is defined at module level (line ~342). On non-Termux platforms _try_termux_ultrafast_version() short-circuits and the function is never called, so the bug stays hidden in CI.

Workaround that fixes it locally: declare the path inside the function instead of relying on the module-level var:

def _print_fast_version_info() -> None:
    from hermes_cli import __release_date__, __version__
    from pathlib import Path as _Path
    _PROJECT_ROOT = _Path(__file__).parent.parent.resolve()
    print(f"Hermes Agent v{__version__} ({__release_date__})")
    print(f"Install directory: {_PROJECT_ROOT}")

Looking forward to the merge — the fast-path version startup is a great improvement. 👍

yyzquwu and others added 3 commits August 2, 2026 19:53
…t label

- _print_fast_version_info referenced the PROJECT_ROOT module constant,
  which is defined AFTER the ultrafast exit point. On current main this
  is a LIVE latent bug: the Termux fast path NameErrors on --version
  (eb40402 changed the print to use PROJECT_ROOT without noticing the
  constant doesn't exist yet on that path). Compute the root locally.
- PR tests asserted the old 'Project:' label; main renamed it to
  'Install directory:' (eb40402). Expectations updated.
Architecture fix for the bug class behind the Termux --version NameError
(live on main since eb40402): version-printing kept being reimplemented
as *_fast() copies at the top of hermes_cli/main.py, each duplicating
canonical logic (project-root resolution, container detection, profile
detection). The copies drift silently — eb40402 edited the canonical
output and referenced the PROJECT_ROOT module constant inside the fast
function, which doesn't exist yet at the fast exit point.

- hermes_cli/_startup_fast.py: THE implementations, stdlib-only. main.py's
  *_fast() names become thin delegates (kept for test/back-compat), and
  PROJECT_ROOT itself derives from the same helper — the constant and the
  fast path can no longer disagree.
- Fast output now includes the .install_method stamp (one cheap file read)
  and a 'Run hermes version for update status' pointer, so globalizing the
  fast path doesn't silently drop slow-path info.
- Guard tests: (1) import-weight — subprocess-imports _startup_fast and
  fails if any heavy module (config/yaml/argparse/cli/run_agent/httpx)
  lands in sys.modules; (2) subprocess parity on+off Termux — the test
  that would have caught eb40402 the day it landed; (3) install-method
  stamp surfacing.

hermes --version: ~3.8s cold / 0.2-0.4s warm -> 0.01-0.02s everywhere.
@kshitijk4poor
kshitijk4poor force-pushed the salvage/37704-fast-version branch from d9fccea to 5759bef Compare August 2, 2026 14:26

@GottZ GottZ left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Summary

Two PRs address the global version fast path and the Termux PROJECT_ROOT failure. #37704 introduced the cross-platform shortcut but duplicated startup logic and missed current output requirements; #62096 rebuilds it around a canonical stdlib-only module with container/profile guards and regression coverage for the reported failure.

Related pull requests

  • #37704 [closed] duplicate — (+186/-8) — superseded by #62096: The diff extends the early version path beyond Termux, but its copied helpers do not resolve the current-main PROJECT_ROOT ordering and output-parity concerns. Despite the earlier keep_open review on #37704, #62096 explicitly addresses those blockers through _startup_fast.py, install-method output, and subprocess guard tests, so the closed PR remains relevant as the credited source implementation.
  • #62096 related — (+475/-40) — keep open with a salvage path: The diff fixes the pre-initialization PROJECT_ROOT failure, centralizes lightweight startup behavior in hermes_cli/_startup_fast.py, preserves container/profile routing, and adds import-weight and Termux/non-Termux subprocess tests. The valuable salvage is the canonical helper module plus the liveness, output, and import-weight guards.

Duplicates

#37704 and #62096 implement substantially the same global --version optimization; #62096 is the documented salvage and structural replacement for #37704.

Suggested consolidation

Keep #62096 open with a salvage path: preserve the canonical _startup_fast.py implementation and its import-weight, output-field, container/profile-routing, and Termux liveness tests. Keep #37704 closed as superseded by #62096; its core optimization is already carried forward with the contributor review’s specific safety and parity objections addressed in the newer diff.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    subgraph Dup37704 ["PRs duplicating each other"]
        P37704["PR #37704 (closed)"]
        P62096["PR #62096 (open)"]
    end
    class P37704 closed
    class P62096 open
    class P62096 target
    click P37704 "https://github.com/NousResearch/hermes-agent/pull/37704"
    click P62096 "https://github.com/NousResearch/hermes-agent/pull/62096"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 2 pull requests and 0 issues in this complex. Each diff was read against this issue; Assessment working set: 27 kB of PR diffs, 8 kB of issue/PR text, 3 kB of discussion (3 comments), 0 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@kshitijk4poor
kshitijk4poor merged commit e4257c1 into NousResearch:main Aug 2, 2026
38 checks passed
@kshitijk4poor
kshitijk4poor deleted the salvage/37704-fast-version branch August 5, 2026 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have type/perf Performance improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants