Commit Graph
83 Commits
Author SHA1 Message Date
Teknium df128c13bb ci: run the WhatsApp bridge node tests in the JS & TS job
scripts/whatsapp-bridge/*.test.mjs (allowlist, delivery_status,
bridge.native, ...) ran in no workflow: the bridge is a standalone npm
package outside the root workspaces, so run-workspace-checks never sees it.

js-tests.yml now installs the bridge's own lockfile (npm ci
--ignore-scripts; two suites import baileys) and runs
`node --test scripts/whatsapp-bridge/*.test.mjs`. The classifier selects
the frontend lane for scripts/whatsapp-bridge/ and
plugins/platforms/whatsapp/, so a bridge-only or adapter-only PR runs it.
No E2E trigger.
2026-10-28 21:06:25 -07:00
unsupportedpastels 26ba38a098 fix(webapp): report the commit an inspection read, and share the detection cases
The gateway inspection reported which halves a source ships but not the
commit it read, so for an unpinned Install from Git the reviewed tree and
the installed tree could differ if the source moved in between. Return
the inspected HEAD as `sha` (a catalog entry's is its reviewed pin) and
carry it through inspectAgentPlugin, so the install can be held to it the
way the native Desktop probe's will be. A cached answer keeps the sha it
read.

The component-detection rules now live in two inspectors (Python and
Electron). tests/fixtures/plugin_component_detection.json holds one set
of cases both run, registered as a shared fixture so editing it runs the
pytest and vitest consumers.
2026-10-27 21:32:38 -04:00
Teknium ca69d857c3 fix(update): a proxy the user set in .env beats the injected system proxy
The Desktop hands `hermes update` the system proxy in both spellings
(HTTPS_PROXY and https_proxy). The .env load replaces only the spelling
the file uses, and urllib and curl prefer the lower-case one, so the
update still went to the system proxy and the .env NO_PROXY stopped
applying. After the .env load, `hermes update` now undoes everything the
Desktop added when .env sets a proxy (the Desktop injects nothing when it
sees one). A .env NO_PROXY alone replaces the user own entries in every
injected NO_PROXY variant and keeps the bypass the Desktop added; a `*`
there drops the injection.

Review S1 (kshitijk4poor) on #144894.
2026-10-27 13:23:31 -07:00
Teknium df8a320f2a ci: route hermes_cli/env_loader.py to the update suites
update_proxy_env now reads which proxy values a .env load wrote, so the
update path imports env_loader and its changes must start the upgrade lane.
2026-10-27 13:23:31 -07:00
Teknium dc8eccf62d ci: route desktop_release_holders.py to the update suites
main_desktop.py (an update entry point) now imports it to name and close a
process holding release/ on a failed swap, so tests/ci/test_update_ci_routing.py
requires it in _UPDATE_DEPENDENCIES.
2026-10-27 11:51:03 -07:00
Teknium 2ff5f4aa2b fix(ci): route desktop_build_reuse to the update and desktop-update suites
source_build imports hermes_cli/desktop_build_reuse.py on the update path, so
tests/ci/test_update_ci_routing.py requires it routed beside desktop_build_lock.py
(its desktop-reuse.mjs script is already covered by the scripts/build/ prefix).
2026-10-27 07:53:07 -07:00
IAvecilla 11a75355a4 fix(docker): hermes webapp reuses the baked bundle, and local Desktop files stay out of the image 2026-10-27 08:41:33 -04:00
Ben Barclay d96591fbc8 fix(config): route config_backend to the upgrade lane and fault-inject the seam's parser in the MoA read test
update_channel now writes through hermes_cli.config_backend, so the CI
classifier lists it as an update dependency. The MoA transient-read test
patched hermes_cli.config.fast_safe_load, which the seam no longer calls;
it now patches utils.fast_safe_load, the parser the FileBackend uses.
2026-10-25 18:54:09 -07:00
Teknium a80b413cd6 fix(git): graph refresh sweeps only object-store debris and lets git clean up
Review round on #144647 (head d016f04694d):

- CI routing: the refresh (slot claim, bounded runner, stale check, detached spawn) moves to
  hermes_cli/git_commit_graph.py, routed to e2e_upgrade with the update pipeline. The update path
  imports it instead of all of worktree_ops; worktree_ops keeps its repack seams and imports the
  shared slot claim and runner from it (no cycle).
- W1: the -w pass runs in whatever repo it was launched from, so its refresh sweeps only the
  object-store locks (clear_stale_git_locks(names=OBJECT_STORE_LOCK_NAMES)); index.lock / HEAD.lock
  there belong to the user's git. The install checkout's update sweep is unchanged.
- W2: timeout/exit reaping SIGTERMs the git tree first and SIGKILLs only after a 5 s grace
  (kill_process_tree(grace=), default unchanged for every other caller), so git deletes
  commit-graph-chain.lock and tmp_graph_*. The age-gated tmp sweep now also takes
  objects/info/commit-graphs/tmp_graph_*.
- N1: a breakaway-forbidding Windows job gets the no-breakaway retry, and a spawn that fails gives
  the 6 h slot back.
- N2: the detached child logs a failed write (git's fatal line) to the home's agent.log/errors.log.
- core.commitGraph=false in the target repo skips the write.
- N3: a successful no-op write stamps hermes-commit-graph.fresh, so an idle repo is not rewritten
  every slot.
- N4: test_update_finish's completion child and every in-process hermes_cli test stub the
  detached spawn; no writer outlives a test run.
- Greptile: the refresh test now runs on a treeless clone over a Bloom graph and asserts the
  write adds no pack (red when --no-changed-paths is dropped).
2026-10-25 17:51:21 -07:00
emozilla 443732a58b fix(update): hermes update rebuilds the Webapp bundle a home serves
hermes update rebuilt the TUI, the web UI and the desktop app but never apps/desktop/dist-webapp.
A gateway with webapp.enabled kept serving the bundle built before the update, which then talked
to the updated backend with an outdated contract.

The update now rebuilds that bundle when any home sharing this install has webapp.enabled, with
prepare_webapp_renderer, the function hermes webapp uses (a current bundle is reused). Like the
other products it is attempted on its own: a failure is reported with the rest at the end and
never skips another product.
2026-10-25 03:22:27 -07:00
Bear Huddlestonandemozilla b5eb5ee7a8 feat(webapp): carry the browser-hosted Desktop's server pieces from #93508
Carried from BearHuddleston's #93508 onto main. The Webapp relay and the
profile-incarnation fence stay out: carried code follows main's
named-profile rule and refuses a missing or deleted profile home.

- Webapp routes: one-use launch tickets, window handoff, and gateway
  WebSocket tickets minted for the signed-in browser
- Browser surface policy and the SPA document policy
- A host terminal for the browser that survives reconnects
- Staged browser uploads and file tickets; file.attach takes a staged upload
- hermes sessions adopt
- Webapp bundle build helpers and the renderer freshness check
- Regenerated gateway contracts

Co-authored-by: emozilla <emozilla@nousresearch.com>
2026-10-25 03:22:14 -07:00
Teknium f05df166b3 ci: route config_version_stamp to the upgrade suite
post_update now imports hermes_cli/config_version_stamp.py (it converts legacy
AGENT_BROWSER_* settings before stamping), so a change to it must start the
e2e_upgrade lane like every other module the update path imports.
2026-10-23 01:43:17 -07:00
yoniebans 3e6589fc34 ci: run the upgrade suite when hermes_state_fts.py changes
The hermes update search-index notice now reads the stored FTS layout through hermes_state_fts.fts_layout_drift, so the module is on the update path.
2026-10-21 04:14:08 -07:00
Teknium d2c6ca2300 test(desktop): E2E guard for turn state after a lost connection
apps/desktop/e2e/core/reconnect-turn-state.spec.ts, real Electron + real
gateway, only the LLM scripted:

1. primary socket blocked mid-turn (loopback proxy that resets and refuses);
   the turn finishes on the gateway while disconnected; after the redial the
   reply renders exactly once (transcript oracle) and no Stop/spinner stays.
2. socket drop while a held turn runs, then a Control+Enter follow-up right
   after the redial: both turns run in order, oracle holds, nothing spins.
3. gateway SIGKILL mid-turn: supervisor brings a new owner up, recovery
   fences the turn `unknown`; the lost-turn card + Discard are on screen
   with "1 Queued — paused"; a follow-up is queued ("2 Queued — paused")
   and does not run ahead; Discard lets it run; oracle holds.

The classifier now routes the queue panel to the core lane, since the
lost-turn card lives there.

Refs #134889 (NF2)
Refs #140337
2026-10-17 11:33:13 -07:00
Teknium 861a0299ed refactor(gateway): no re-export shims for the split status/base helpers; the lock split is one-way
The round-1 health offset moved safe_url_for_log and the scoped-lock
release pair into siblings but re-exported them from gateway.status /
gateway.platforms.base, and status_scoped_locks read gateway.status
privates at call time while gateway.status re-exported the sibling's
privates (_unusable_lock_dir, _warned_unusable_lock_dirs). AGENTS.md:
no re-export shims for internal moves.

- gateway/status_lock_dir.py: the unusable-lock-dir report
  (ScopedLockDirUnusable + public unusable_lock_dir / _recovered /
  reset / reported). gateway.status imports it; it imports nothing back.
- gateway/status_scoped_locks.py: only the release pair, reading
  gateway.status at call time; gateway.status no longer imports it.
- Every importer points at the real home: base.py, run_bootstrap.py,
  the Feishu and Slack adapters, and the tests that patch them
  (gateway.status_scoped_locks.release_*). safe_url_for_log comes from
  gateway.platforms.base_log_safety; base.py imports it only for its
  own use.
- classify_changes: status_lock_dir.py rides with gateway/status.py on
  the e2e_upgrade lane.

code_health: 0 blocking (status.py stays under its main value).

Review finding by @unsupportedpastels on #140490 (minor 4) and by
@alt-glitch on #140490 (suggestion 4)
Refs #134889 (W2)
Refs #140337
2026-10-17 08:03:53 -07:00
Teknium 11380ac90a ci(routing): route hermes_dir_acl.py to the e2e_upgrade suite
hermes_cli/update_cmd_maint.py and update_host_obligation.py import the new
root module, so the update path runs it; list it beside its root siblings
(hermes_bootstrap.py, hermes_constants.py) in the update owners.

Refs #134889 (W8)
Refs #140337
2026-10-17 08:03:52 -07:00
Teknium 275b15fc35 ci: catalog-only PRs run the catalog tests instead of the full suite
A PR that changes only plugin-catalog entry YAML (plus contributor email
maps) ran all 23k Python tests on Linux, macOS and Windows, ~25 min, and
unrelated core flakes kept blocking one-file catalog additions.

The classifier now has a `catalog` lane. A diff made only of catalog entry
data turns `python` off and `catalog` on; ci.yaml then runs
catalog-tests.yml (every test file that reads the catalog or the email
maps, discovered by scripts/ci/catalog_test_files.py), plus the contributor
check. The lane is required by all-checks-pass whenever detect selects it.
plugin-catalog/README.md keeps the full suite (a docs-page test mirrors
it), and any non-catalog file in the diff brings the suite back. The
structural and pinned-source admission gates in plugin-catalog-ci.yml are
unchanged.
2026-10-16 04:37:07 -07:00
Teknium cc5928bd73 Merge origin/main (#44: 15 commits to 1fd1827726) into feat/unified-gateway-runtime 2026-10-11 14:02:25 -07:00
Teknium 8f05f890c5 ci(remote-backends): debian 13 for the test sshd, container setup as a mounted script
Review nits from #136602: the sshd and docker-backend image moves to the
current stable debian:13-slim (digest-pinned index), and the inline bash -c
setup becomes scripts/ci/test_sshd_container_setup.sh, mounted read-only and
run in the container. The new script joins the remote_backends path gate.
2026-10-11 11:55:56 -07:00
Teknium 438d10200c ci: run the SSH and Docker terminal backends against a real sshd and daemon
The SSH backend's real-host tests skipped on every CI run (nothing set
TERMINAL_SSH_HOST), and every Docker backend test mocked subprocess or the
docker CLI, so no CI run had executed either backend end to end.

- remote-backends.yml: starts a throwaway sshd container on 127.0.0.1 with a
  per-run keypair (scripts/ci/start_test_sshd.sh) and runs
  test_ssh_environment.py plus the new test_docker_backend_real.py against it
  and the runner's Docker daemon with a digest-pinned debian:12-slim. A file
  that skips fails the job, so the lane cannot go green over zero coverage.
- New classifier lane `remote_backends` (tools/environments/, tools/terminal_tool*,
  the real tests, the sshd harness, run_tests.sh). ci.yaml calls the workflow
  only when it fires, plus every push to main; required_results makes a selected
  lane mandatory, like the update consumers.
- The real tests read HERMES_TEST_SSH_* / HERMES_TEST_DOCKER_IMAGE (forwarded
  by run_tests.sh), because the hermetic conftest blanks TERMINAL_SSH_KEY as a
  credential before any test can read it.
- New real SSH cases: exit code/stderr, cwd persistence with spaces vs per-call
  workdir, quoting across the ssh hop, SendEnv passthrough through a real
  AcceptEnv, file tools write/read on the remote.
- New real Docker cases: exec + exit codes, cwd vs workdir, exported env
  persistence, docker_env/docker_forward_env reach the container and
  nothing else does, quoting, file tools write/read, /root surviving a
  container replacement.
- The existing one-shot test is xfail(strict): TERMINAL_SSH_PERSISTENT=false
  is read into ssh_config["persistent"] but SSHEnvironment never consumes it,
  so every SSH session persists exported env. Its _cleanup also cleaned the
  raw task id, not the resolved env key, so one test's env leaked into the next.
2026-10-11 11:55:56 -07:00
Teknium 760912ea8c ci: run the native-reply crash recovery test in the messaging job
tests/gateway/test_native_reply_crash_recovery.py (81edebdb07) drives the
real python-telegram-bot SDK and skips in the ordinary pytest slices, which
omit the messaging extra. test_messaging_gated_selection requires every such
file to be named by the e2e job's "Run messaging-gated gateway cases" step
and to start the e2e lane when edited; add it to both.
2026-10-09 19:43:16 -07:00
Teknium 2f78c72345 ci: route hermes_state_holders.py to the upgrade suite
The round-7 downgrade guard (hermes_cli/update_downgrade_guard.py) is on the update path and reads
state.db through hermes_state_holders.read_only_db_uri, so an edit to it must start e2e_upgrade
(tests/ci/test_update_ci_routing.py).
2026-10-09 02:54:01 -07:00
Teknium 607a23e858 Merge origin/main (#30: 54 commits to 1744a19e0d) into feat/unified-gateway-runtime
8 conflicts, all from main's lint sweep (#135281 "Harder linter fixes" + the RUF series:
implicit-Optional, unused-unpacked-variable, next(...) for [...][0]). No other main commit touches
a conflicted file. Resolution: the branch side of each file plus main's exact autofix
(ruff 0.15.10, main's ruff.strict.toml, --fix --unsafe-fixes) on it. ruff.strict.toml takes main's
copy (the newly enabled rules). tests/cron/test_bot_chat_delivery_child_env.py stays deleted
(1cc9cb9199 retired its lane).
2026-10-08 21:38:54 -07:00
Teknium ffcd3621a9 ci: E2E suites run only on release builds
Pull requests and pushes to main no longer run any E2E suite: ci.yaml's detect gate
forces every e2e* lane off unless the run is a stable-release run (inputs.release) or a
manual dispatch, whatever the diff, the update-path classifier or the run-e2e label
selects. That covers the Python e2e and e2e-upgrade jobs, the Windows E2E and Windows
install + update journey in tests-os, and the Desktop core/update suites.

install-e2e.yml drops its pull_request (path-filtered `pr` route) and 12-hourly
schedule triggers; it runs from the stable-release gate (workflow_call) or by dispatch.
live-providers drops its nightly schedule and keeps the release-tag push + dispatch.

Why: a one-row change to left_core_migration.py held a PR for 40 minutes on the
Windows install + update E2E; the suites cost most of the larger-runner minutes and
the release gate already runs all of them strictly.
2026-10-08 19:48:31 -07:00
Teknium ab386ebd5f ci: run the messaging-gated gateway cases in the messaging-equipped e2e job
The ordinary `test` slices install no `messaging` extra, and the e2e job,
the only one that does, selects only tests/e2e. Four gateway cases probe for
the REAL Discord/Telegram distribution and skip without it, so they ran
nowhere: both tests in test_native_role_reauthorization.py,
test_telegram_fifo_unknown_and_current_authorization_survive_sigkill and
test_crash_mid_turn_then_platform_message_gets_one_pause_notice_and_reset_unpauses.

The e2e job gains a step that runs those three whole files (run_tests.sh drops
::node selectors) with -rs and fails when the summary reports any skip, so a
missing SDK can never read as green. The classifier starts the e2e lane when
one of them is edited. A CI test derives the gated set from the source (any
tests/gateway file probing PathFinder.find_spec for discord/telegram/slack)
so a new gated file must be added to the step.

Local run of the step: with the messaging venv 5 passed, 0 skipped (rc 0);
without it 1 passed, 3 skipped -> ::error:: and rc 1.

Review: https://github.com/NousResearch/hermes-agent/pull/106742#issuecomment-6067585232
2026-10-08 19:09:39 -07:00
Teknium a06c82088f ci: route web_build_limits.py to the update e2e lanes
Main (#133931) made source_build.py import hermes_cli/web_build_limits.py
without adding it to _UPDATE_DEPENDENCIES, so tests/ci/test_update_ci_routing.py
is red for e2e_upgrade and e2e_desktop_update on every merge ref that includes
it. Same one-line route main's fix/ci-routing-web-build-limits (6b5a089a00e)
adds.
2026-10-07 16:29:16 -07:00
Teknium 134e08ca6d fix(ci): route web_build_limits.py to the update e2e lanes
#133931 made source_build.py import hermes_cli/web_build_limits.py but did not add it to
_UPDATE_DEPENDENCIES, so tests/ci/test_update_ci_routing.py has been red on main and on
every PR whose merge ref includes it: a change to the module would skip the
e2e_upgrade and e2e_desktop_update lanes that run the build it shapes.
2026-10-07 15:26:14 -07:00
Teknium 28cb481f28 ci: route backup_cron_prompts.py with the update-path backup modules
The code-health split moved restore_cron_prompt_fields_if_degraded out of backup.py;
update_cmd_config imports it, so the update E2E suite must start when it changes
(tests/ci/test_update_ci_routing.py).
2026-10-07 11:37:25 -07:00
Teknium 926b207b74 ci: route local-gateway.ts (gateway ensure code_sha check) where host-backend-attach was
The pool-era host-backend-attach.ts is deleted on the branch; the attach-time version check
it carried now lives in local-gateway.ts (ensure compares code_sha with the checkout).
2026-10-07 07:33:07 -07:00
alt-glitch cbffbeecf4 ci: package __init__ files on the update path start the update E2Es
Importing a/b/c.py runs a/__init__.py and a/b/__init__.py first. The
update-lane routing listed hermes_cli/local_runtime/processes.py (the
v2026.9.24 updater imports it after the pull) but not the package init
that import runs. #134116 changed what that init loads, its PR never ran
e2e-upgrade, and main went red until #134242.

classify_changes now routes the package inits above every module the
e2e_upgrade and e2e_desktop_update lanes own, and above each shared hub,
because a hub's inits run whenever the hub is imported. An init that is
itself a hub stays out. The routing guard's import walker counts the
package inits a module's imports run, so the next unrouted one fails it.
This routes eight inits that were missing: local_runtime, gateway, cron,
tools, tools/environments, plugins, hermes_platform,
hermes_cli/observability.
2026-10-06 20:30:06 -04:00
Teknium b5a5b9b086 ci(update): route the inline-source parser and retire the landed siblings' allowance
Two ratchets this suite arms for its own siblings fired once they landed:

- the Windows pause (#132338) now imports gateway/status_inline_source.py
  to recognise inline-bootstrapped gateways, so a change there starts the
  e2e_upgrade suite like gateway/status.py (only two importers: not a hub)
- the marker-corpus consumers #132345 and #132354 ship are on main, so
  _LANDS_IN_SIBLING_PR empties as its comment asks
2026-10-06 13:02:14 -07:00
Teknium 9f8d7bf604 ci: route the ZIP recovery module and the SSH gate's corpus test like their siblings
`hermes_cli/_early_recovery_zip.py` (split out of `_early_recovery.py` on the
commit-point PR) is imported by the update path, so a change to it must start
the update pipeline and install E2E suites. Both lists and the install-e2e path
filter now match the `_early_recovery` prefix, which also routes any later
sibling. `remote-lifecycle-v2-marker.test.ts` reads the update-marker corpus
and is now a listed consumer; `remote-update-marker-programs.test.ts` exists in
this tree, so its sibling-PR allowance is gone.
2026-10-06 13:02:14 -07:00
Teknium fdae2fc175 fix: plugin-install modules no longer start the update journeys they never reach
Replaying the last 131 non-merge main commits (4787e4d56f) through the
classifier: main selects e2e_upgrade 5 / e2e_desktop_update 3, the CI branch
12 / 10. Four of the seven new hits per lane touched only plugins_cmd.py or
plugins_cmd_install.py. migrate_all_homes reaches them only when a home's
configured memory provider left core (the catalog install); no upgrade or
Desktop update journey configures one, so those suites cannot observe the
change, and the unit lane already runs on every .py edit. Measured on a
fresh home: memory_provider_migration, pm/plugins_state, agent/memory_provider
and plugins/memory run; plugins_cmd_install executes only its module body,
plugins_cmd is never imported.

Kept, because a fresh-home update runs them: memory_provider_migration.py
(migrate_all_homes on every source build), plugins/memory/__init__.py (the
post-update Honcho profile sync), pm/install.py (sealed() /
lazy_installs_allowed() gate every update's default-tool install), and
scripts/build/ (frontend-common.mjs is imported by freshness/web/tui/desktop).
After: 8 / 6. A guard test fails if the journey-home migration starts running
plugin-install code, the signal to route those modules again.
2026-10-06 13:02:14 -07:00
Teknium 8d734420da ci: route hermes_platform.resolver to the upgrade suite
update_cmd_commit._admitted_python now resolves python3.N through hermes_platform.resolver
(cd471c9c55a), so the update path imports the resolver package and a change to it must start the
upgrade E2E, like hermes_platform.host already does.
2026-10-06 13:02:14 -07:00
Teknium 3623e9f3c8 fix(ci): route the partial-clone pack tidy to the upgrade suite
What: scripts/ci/classify_changes.py names hermes_cli/git_pack_tidy.py in the update pipeline, so a
change to it runs e2e_upgrade.
Why: the derived routing guard (test_every_module_an_update_entry_point_imports_starts_its_suite)
found it imported by the update path and unrouted: main's partial-clone tidy (a0f47657b9) runs in
every update's pre-fetch via update_cmd_check.
2026-10-06 13:02:14 -07:00
Teknium ae91994515 fix(update): CI routes each update-path file to the suites that exercise it
- hermes_cli/web_routers/status.py (review CI2): /api/health `commit` is what
  host-backend-attach.ts compares to the checkout before attaching after an
  update, yet an edit ran only the python lane. Now also e2e_desktop_update,
  the lane host-backend-attach already starts.
- Tauri updater (review CI3): marker.rs started e2e_upgrade and
  e2e_desktop_update, neither of which builds or runs the bootstrap installer
  (no workflow of either lane touches apps/bootstrap-installer or cargo),
  while its callers update.rs/paths.rs started none. All three now route as
  one unit to the lane that compiles and tests them: rust (cargo test, via
  .rs) and nothing slow. _TAURI_UPDATE_MARKER is gone.
- remote-update-marker-programs.ts (+ test) (review CI3): the SSH remote's
  marker judge/gate ran only frontend; it is the marker.sh/marker.ps1
  contract run remotely, and its importers (remote-lifecycle.ts,
  managed-ssh-update.ts) are desktop_updater files, so it is one too.
- gateway/status (review CI3): the prefix also matched status_phrases.py
  (turn-status wording); the named status_inline_source sibling does not
  exist. Tightened to gateway/status.py.

Changed assertion: _R6_CASES pinned marker.rs -> (rust, e2e_upgrade,
e2e_desktop_update); the finding is that the two e2e lanes are the bug (they
never build marker.rs), so it now pins (rust,), and a new test pins the
three Tauri updater files off every slow lane.
2026-10-06 13:02:14 -07:00
Teknium d153acfe85 fix(ci): route the update path's new sibling modules and the SSH marker corpus reader
The stack's health refactors carved main_desktop_tcc.py out of main_desktop.py
and status_inline_source.py out of gateway/status.py; both are imported on the
update path, so an edit to either must start e2e_upgrade (and, for the desktop
module, e2e_desktop_update). Route each facade by prefix so its <stem>_*
siblings are covered without naming files this branch does not have yet.

The desktop gate's SSH-v2 marker fix added remote-update-marker-programs.test.ts,
which reads the shared marker corpus: list it as a consumer, allowed absent until
#132345 lands, like the other gate-PR consumer.
2026-10-06 13:02:14 -07:00
Teknium 259fe73b0b fix(ci): route update dependencies and reject selected consumer skips
Route the exercised subprocess and provider-migration dependencies to both
real-update journeys. Keep backup routing under an explicit dispatch test.
Use the aggregate's existing detect outputs to reject skipped or missing
update consumers while retaining legitimate unselected PR skips.

Verified with the real classifier and aggregate subprocesses against
b9dec76a042, composite ff5c8f1a64b, and fixed code. Canonical runner:
571 passed, 0 failed, 1 native-Windows skip across CI and marker corpora.

(cherry picked from commit a082ca3c62ed49382f173a07882d4280d0ba7c9c)
(cherry picked from commit ed9f2028256b8b1d6af81ef97cd472f30d2cb042)
(cherry picked from commit 6d5cc740b6a75d476802222c2f4754855a5712b0)
2026-10-06 13:02:14 -07:00
Teknium f469749922 ci: route the remaining update-path files to every suite that exercises them (m11)
- apps/desktop/electron/update-marker-gate.ts -> desktop_updater (the gate's
  live-marker probe is the script's marker contract); e2e_desktop_update as before
- apps/desktop/electron/remote-lifecycle.ts -> desktop_updater besides frontend
- apps/bootstrap-installer/src-tauri/src/marker.rs -> e2e_upgrade and
  e2e_desktop_update besides rust (the Tauri claim honours the held-checkout rule)
- hermes_cli/_launchers.py -> e2e_desktop_update besides e2e_upgrade

(cherry picked from commit b9dec76a042c71d29ded5dda35090a382645d9d3)
(cherry picked from commit 9dfbb21f5c03f56ffd0c7790995585641861d6d3)
2026-10-06 13:02:14 -07:00
Teknium 2d7307931d ci: a shared fixture selects every lane that runs one of its consumers
tests/fixtures/update_marker_corpus.json is read by pytest (update_lock),
cargo (marker_tests.rs), vitest (update-marker-corpus.test.ts) and the
marker.sh / marker.ps1 corpus tests. By path it selected only python, so a
corpus edit never ran the Rust, Electron or Windows PowerShell readers.
_SHARED_FIXTURE_CONSUMERS routes a fixture with its consumers' test lanes;
the routing test derives the consumers from the tree and replays the
classifier through the workflow files to each lane's job (review D14).

(cherry picked from commit 4893ef31833b5fcbdfc527beb70724ea63a3eb8f)
(cherry picked from commit 20c04a27b2a04ebaabb9a9df3693339243d9380e)
2026-10-06 13:02:14 -07:00
Teknium 81a74ae0c8 ci: route left_core_migration to the update suites
main added hermes_cli/left_core_migration.py, imported by source_build.py; the
R15 import-graph test caught it unrouted on the composite of all seven PRs.

(cherry picked from commit 338aabcdda316a92b8bfcae10f52730fd6fcb344)
(cherry picked from commit 5a839bb475d08f82293f07bcdb60ded859814476)
2026-10-06 13:02:14 -07:00
Teknium 5a36d5ac34 ci: route update owners by the import graph and dispatch every set lane
The classifier missed modules the update entry points import
(_subprocess_compat, desktop_build_lock, memory_provider_migration, the
scripts/build compilers) and never sent source_build/source_completion to
the Desktop update suite. ci.yaml gated tests-os on `python` alone, so a
TypeScript-only change to the Electron marker/result readers set
desktop_updater and still never reached the Windows hand-off tests.

- classify_changes.py: _UPDATE_ENTRY_POINTS / _DESKTOP_BUILD_ENTRY_POINTS,
  an owner list of what they import, and an explicit _SHARED_HUBS set.
- tests/ci/test_update_ci_routing.py reads the entry points' real imports
  (AST incl. lazy imports; ESM import/step() closure of the build scripts)
  and fails until each is routed or is a real hub (>= 25 importers).
- It also replays the REAL classifier's output through the composite
  action, ci.yaml detect outputs, job if:/with:, the called workflows' job
  if: and the rendered Windows step, for every combination of the gated
  lanes (tests/ci/_gha_expr.py models the expression subset and raises on
  anything else).
- ci.yaml: tests runs for python|e2e|e2e_upgrade; tests-os for
  python|desktop_updater|e2e|e2e_upgrade.

Red on 8cc78279a36: 30 of the new routing tests fail (R15 table cells,
16 lane combinations, ownership/closure tests).

(cherry picked from commit 3b17b5d2ad8081534bbeefcf5d8334cd79a7e6fd)
(cherry picked from commit 594f8b0c4ba32324da922d5289b0932299ffe7a9)
2026-10-06 13:02:14 -07:00
Teknium 424d52c524 ci: start the real-update suites for every file on the update path
The e2e_upgrade lane (Linux e2e-upgrade + the Windows install + update
journey) only matched the update_* module family, so a PR touching
cmd_update in hermes_cli/main.py, _early_recovery.py, venv_sync.py,
relaunch.py, the gateway restart/pause surface, hermes_bootstrap.py or
the Desktop hand-off scripts merged without a real `hermes update`. The
e2e_desktop_update / desktop_updater lanes likewise missed main.ts, the
hand-off result reader, the install-kind / attach modules,
desktop_update_verify.py and main_desktop.py.

The added paths are the steps of the update pipeline (updater audit
code maps, step tables) plus the lock / marker / recovery state the
update_* modules import. Two invariant tests pin every such file to its
lane and fail when one is renamed.

(cherry picked from commit b02e2cc124e74033ccb4c6e1c7c939590ef65fac)
(cherry picked from commit 16068789ea98ce55e92eeb148ec9345d6778e781)
2026-10-06 13:02:14 -07:00
Teknium 519a13aaa6 ci: Desktop session open/resume changes run the core E2E
Every core spec drives open, resume and switch, yet changes under
apps/desktop/src/app/session/ skipped the suite on the PR; #132017
changed resume that way and main went red on remote-secondary.
2026-10-05 23:36:55 -07:00
Brooklyn Nicholson 158fd638da test(desktop): every rail layout keeps a door to the active default; rail changes run the core E2E
A layout matrix (single/fleet × strip/condensed × local/remote active,
starting from a named profile or the all view) asserts the active
gateway's default is one click away in each. The four fleet+condensed
cases fail on main.

The core E2E lane only starts on PRs touching its guarded paths, so a
profile-rail edit would have met fleet-condensed-default.spec.ts only on
main. Add the rail sources to the lane.
2026-10-03 17:17:25 -05:00
ethernet 059efe8b29 ci: replace the ci-reviewed label gate with CODEOWNERS
Path-based review (workflows, composite actions, eslint/prettier config,
the MCP catalog, install-hook files) moves to .github/CODEOWNERS. GitHub
requests and enforces that review, so CI no longer classifies those paths,
checks a label, or reruns failed jobs when the label is added.

Removes review-labels.yml, emit_review_status.py, the rerun-review-labels
job, and the ci_review / ci_review_files / mcp_catalog classifier outputs.
The run-e2e rerun job in label-rerun.yml stays.

The supply-chain scan keeps its content patterns (base64 decode + exec,
obfuscated subprocess), which CODEOWNERS cannot express. It fails on its
own again, as it did before #68833 routed it through the label gate. Its
.pth and install-hook path checks are dropped in favour of CODEOWNERS.
2026-10-02 15:22:49 -04:00
teknium1 d802c47238 docs: one complete plugin catalog submission guide, synced with the README rules
Submitters only saw a stale 5-point summary on the catalog page (it even listed
a 'Released' requirement nothing enforces), while reviewers applied ten README
rules plus criteria written nowhere public (credentials, approvals, disclosure,
compatibility metadata, forks of bundled plugins).

- plugin-catalog/README.md: rules 11-15 write down those review criteria; the
  rule list sits between admission-rules markers.
- New developer-guide page 'Submitting to the plugin catalog': before you
  submit, the PR flow, the full rule block (verbatim mirror), pin updates,
  delisting vs removal, asking for a hook.
- The /plugins 'Submit' links and the user-guide section point at it.
- tests/website/test_catalog_rules_mirror.py fails when the two rule blocks
  differ; classify_changes makes a README-only edit run it.
- AGENTS.md, plugins/AGENTS.md and CONTRIBUTING.md reference both files.
2026-10-01 21:58:11 -07:00
emozilla 5c08ad68f7 ci: run the slow lanes on a PR only when it touches their paths
The E2E suites, the Windows E2E jobs, the Docker image build and the Nix
flake check took about 120 of the ~140 larger-runner minutes each PR push
spent, and fired on nearly every PR. The 32-core pool backed up behind
them.

On a pull request each slow lane now runs only when the diff touches the
suite, its harness, or the code it guards (classify_changes.py
_E2E_LANES, _DOCKER_PATHS, _NIX_LANE_PATHS), or when the PR carries the
run-e2e label. Every push to main still runs all of them, and a release
run still forces every lane on.

Adding run-e2e reruns the head commit's CI, Docker and Nix runs; detect
reads the live labels because a rerun replays the original event.

Replayed over the last 200 merged PRs, the modelled 32-core cost of one
push drops from about 120 to about 10 runner-minutes.
2026-09-30 14:37:52 -04:00
Mark S. ca705dbf7e fix(ci): card-tool contract test reads the as-const list and runs on desktop edits (#128010)
#127828 changed CARD_TOOL_NAMES in apps/desktop/src/lib/tool-render-class.ts
from `new Set([...])` to `[...] as const`. The pytest that pins that list
against the gateway's _TOOL_LIFECYCLE_UI_TOOLS located it with a regex that
only matched `new Set([`, so it has failed on main since with
"CARD_TOOL_NAMES not found in tool-render-class.ts". The gateway set still
covers every desktop card name; only the test's parser was stale.

The PR merged green because it touched only apps/, which skips the Python
lane, so the one test that reads that file never ran on it.

- The parser accepts both declaration forms and fails on an empty list
  instead of passing a vacuous subset check.
- tool-render-class.ts joins _PY_RELEVANT_CONTRACT_FILES so an edit to it
  runs the Python lane, with a classifier case for the entry.
2026-09-29 12:27:26 -04:00
ethernet 428d2d6a86 ci(classify): tests/_fixtures re-arms the desktop-updater lane
The conftest split moved its platform gating and live-system guard into
tests/_fixtures/. tests/conftest.py re-arms the desktop-updater lane
because it owns that skip logic; the modules it now delegates to must
too, or an edit there skips the lane that exercises it.
2026-09-24 14:40:09 -04:00