Commit Graph

270 Commits

Author SHA1 Message Date
ethernet
c58744e59e fix(release): link the draft at cut time, not a page that 404s until green
The stable cut already creates the draft on the claim ref before it
dispatches the gate, but the output pointed at releases/tag/<claim> and
then at releases/tag/v<version> "when it is green". GitHub serves a draft
only at an untagged-* URL, so neither link showed it. Operators read that
as "the draft appears after the build".

Take the URL gh release create prints (the release's html_url) and print
it up front, with a note that notes edited during the build survive:
edit_draft_release keeps the body and strips only the warning fence. The
v<version> URL stays, labelled as where the release lives once published.

The canary resume path had the same broken releases/tag/<tag> link; it
now uses the create output or the url field of gh release view.
2026-09-23 19:20:27 -04:00
ethernet
0384a24edc Merge branch 'ethie/release-attempt-refs' into ethie/pm-clean
Conflicts:
- scripts/releases/stamping.py, tests/scripts/test_version_stamping.py:
  took ethie/pm-clean. The release branch's side was only its base's copy
  of "stamping a payload snapshot skips the bootstrap-installer check"
  (8411fdb333, same patch-id as 8d34601f47 here); the install-stamp
  refactor c13ea774e6 supersedes the rest.
- tests/ci/test_stable_release_graph.py: kept pm-clean's release-epoch
  contract (no HERMES_RELEASE_EPOCH on termux-deb, version on docker and
  nix only) and the release branch's per-group receipt wiring.

Semantic conflict: the dispatch log step (6e64e961d8) read
inputs.termux_only, which the jobs input replaced. It reads JOBS now, and a
dispatch that selects only some groups has no release.py replay, as a
termux-only one had none before.
2026-09-23 19:00:44 -04:00
ethernet
babbec1c4c feat(pm): isolate developer test environment from runtime extras 2026-09-23 18:13:38 -04:00
ethernet
9a3fcf9756 fix(install): pass --no-registry to every bootstrap uv python install
install.ps1 already kept uv's bootstrap Python out of the Windows
registry, but setup-hermes.ps1 did not, so every Windows dev checkout
registered that interpreter under HKCU. The flag is Windows-only; the
POSIX installers take it too so every bootstrap issues the same command.
2026-09-23 18:01:43 -04:00
ethernet
7c41603da9 test(install): installer contracts follow the rerun and invocation fixes
- setup/gateway stages run under a real controlling terminal; with none
  (curl | bash, Docker builds) they are skipped rather than failed.
- The repository-failure frame fixture has a commit; a commitless checkout
  is an interrupted clone that gets re-cloned from the network.
- The unmerged-index fixture merges with an explicit identity; CI has no
  git identity, so the merge never started and no conflict existed.
- The ps1 Python stage expects --no-registry on uv python install.
- pm/native_build.py finds PowerShell on the environment's own PATH, as
  pm/shell.py does for bash; allowlisted alongside it.
2026-09-23 17:51:29 -04:00
ethernet
13ebc163a1 ci: fold the PowerShell installer job into the tests-os Windows lanes
installer-tests.yml predates nothing it still owned. Its pytest step
(test_source_launcher_stages.py) is platforms("windows") and already runs in
both tests-os Windows lanes, so every installer PR ran it twice. The
`installer` lane never gated anything on its own either: every path that set
it also sets `python`, which gates tests-os.

The two standalone scripts/tests/*.ps1 suites become one platforms("windows")
pytest file parametrized over Windows PowerShell 5.1 and pwsh 7, so
list_os_marked_tests picks them up with everything else. The `installer` lane
goes away from the classifier, detect-changes, ci.yaml and the
all-checks-pass gate; the classifier contract now pins that install.ps1 and
its suites turn `python` on.
2026-09-23 17:19:29 -04:00
ethernet
eb877f88c5 fix(release): check the held Store submission instead of releasing it
The Partner Center submission API has no call that releases a held
(Manual) submission, and update refuses a committed one, so the release
step's PUT and re-commit could not work. The publication pass now only
reads the submission: a certified one prints a GitHub warning to click
Publish now in Partner Center, one still in certification says what comes
next, a live one is a no-op, and a failed one leaves the run red.

It finds the held submission through pendingApplicationSubmission on the
app, instead of posting a probe submission and scraping an id out of the
409 error. The HTTP runner also sends the request headers it is given:
before, every API call after the token went out without Authorization.
2026-09-23 17:14:46 -04:00
ethernet
20f3b5f38b feat(release): start each install arm from its own receipt
transitions splits into one job per receipt (darwin-arm64, darwin-x64,
win32-bundle), and each packaged install job waits only on its own. The
Mac install arms no longer wait for the other arch or for the smokes.

candidate-manifest moves into stable-release.yml and waits for every
candidate call, so it still runs after every smoke (decision 23). The
smoke results it records are the calls' own results, mapped to the smoke
job names the final manifest requires. publish-bundles and complete read
its digest again, which the per-group split had left unset.

read_manifest resolves its opener per call instead of binding
urllib.request.urlopen as an import-time default, so the process trust
setup applies. The receipt fixtures gain the runner's RUNNER_TEMP and the
baseline's macOS identity.
2026-09-23 17:08:59 -04:00
ethernet
427d4936c2 test: retarget merge-fallout tests at pm-clean's seams; drop tests of retired code
The merges from main kept or added about 45 test files written against the
legacy updater and installer seams that pm-clean replaced. Each failing test
was checked against the current code:

- Deleted, because every test targets code that pm-clean removed or that is
  only reachable from dead or frozen old-updater code:
  update_orphan/handoff_backend_reap, handoff_desktop_rebuild,
  interrupted_recovery (it also launched a real completion against the live
  checkout), update_stale_virtualenv, update_venv_health,
  verify_core_dependencies, lazy_refresh_venv_repair,
  certifi_repair (already dropped in 2f20ceb6f7; the merge resurrected it),
  banner_git_state (covered by test_source_check).
- Retargeted where production reads now:
  - version identity via version_info.get_code_identity
  - update receipts via a ContextVar scope
  - pm.extras / pm.installed_package / pm.ensure_import for matrix,
    computer_use, fal and tirith
  - install hints via pm.install_hint
  - the lock pins in pm/lock.json
  - probe_root for the critical-module probe
- Fixture repairs:
  - git-committed trees for source stamps
  - activate's real `--runtime-only` argument
  - a semver install stamp for requires_hermes gates
  - the Windows gateway restart after a verified update
  - snowflake-length ids that cannot collide with the runner uid
- Collection fix: one platforms() marker per test in gateway_proc_fallback.
2026-09-23 16:55:09 -04:00
ethernet
1038f8479c fix(install): make both installers survive their real invocation paths
Review findings against the pm-clean installers, each reproduced first:

- install.sh: `curl | bash` aborted before main under `set -u` (empty
  BASH_SOURCE). The entry guard falls back to $0.
- install.sh: setup/gateway read stdin, which under `curl | bash` is the
  script itself. They open /dev/tty when a terminal can be opened, and
  otherwise skip with guidance.
- Both: any uv on PATH was trusted. uv 0.6.17 has no `python install
  --no-bin`. A PATH uv now has to run and be at least the pinned version,
  otherwise the pin is staged.
- install.sh: the staged uv went under ~/.hermes/tools even with a custom
  --hermes-home. It now goes to pm's store_root() default,
  $HERMES_HOME/tools.
- Both: when a stash failed, the script logged "overwritten below" and ran
  `reset --hard` anyway. Local work is now parked before checkout, and a
  stash failure stops the install.
- Both: reruns ignored an explicit HERMES_REPO_URL. It now repoints origin.
- Both: --commit had no ancestor guard. The pin must be on the installed
  branch.
- install.sh: the blobless fallback was `--depth 1 --single-branch`, so a
  non-tip --commit could not check out. It now keeps full history with
  blobs fetched on demand.
- Both: ported main's recovery for a commit-less .git (moved aside, #40998)
  and for an unmerged index (reset -q before the stash, #4735). Stashing
  before checkout makes both reachable.
- install.ps1: on Windows PowerShell 5.1, `native 2>$null` / `2>&1` under
  Stop turns stderr into a terminating NativeCommandError, verified on a
  Win11 host. Every native call now goes through Invoke-Native, which
  relaxes the preference only for that call.
- install.ps1: clone publishes from a staging dir, with retries and a
  blobless fallback, and refuses a non-empty destination (mirrors
  install.sh). UV_NO_CONFIG and `--no-registry` are restored. pwsh 7
  HttpRequestException falls back to the mirror, except TLS trust failures.
  tar resolves from System32. A literal CR/LF in the desktop failure
  message is removed.

Deletes six tests that regex-extracted main's legacy install.sh functions
(node, browsers, PATH block, lockfile churn; pm runs `npm ci` whenever a
lockfile exists). The two behaviours still relevant are covered by new
behavioural tests.
2026-09-23 16:28:00 -04:00
ethernet
c98bdb77f5 fix(pm): prepare the Windows ARM64 compiler environment for every source dependency build
cryptography ships no win_arm64 wheel, so every Windows ARM64 venv sync
compiles it from the sdist and needs MSVC, Clang, Rust and static OpenSSL.
Only setup-hermes.ps1 (and so activate.ps1) prepared that environment,
between a `pm install --tools-only` and the real sync. install.ps1,
`hermes update` and repair ran the same sync without it and failed in
openssl-sys.

PM owns the sync, so PM prepares it. pm/native_build.py holds the adapter
(moved from scripts/build/windows_deps.py) plus source_build_environment(),
which prepares only on win32-arm64 when the synced project carries the
provider script. A payload has prebuilt dependencies and needs no compiler.
VenvPackage.apply and build_environment pass the result to uv children
only. It carries the bridged pip index settings, which managed_environment
applies only to the ambient environment. The state root stays the store
parent, so existing vcpkg/OpenSSL builds are reused.

setup-hermes.ps1 collapses to one `pm install`: the tools-only split existed
only for this preparation, and pm install already puts its tools on PATH
before the venv sync (pm/cli.py activate check).

Not yet verified live on Windows ARM64.
2026-09-23 16:07:47 -04:00
ethernet
fd49308813 fix(ci): the install.sh protocol lane verifies without a source stamp
The products stage (source_completion) writes install-stamp.json, and
this lane deliberately runs only prerequisites/repository/config/complete,
so the checkout never has one. The lane now says so with
--no-source-stamp; full-install callers (windows-e2e.ps1) stay strict.
Replayed locally: the four stages leave no stamp, the old invocation
fails exactly like CI, the lane invocation verifies.
2026-09-23 16:05:55 -04:00
ethernet
6aaff62961 fix(install): verify a tagless checkout's commit-only source stamp
The source completion tail stamps baseVersion from the checkout's
reachable release tag, which is null on a PR checkout, and the bootstrap
verifier demanded a non-null baseVersion, so install.sh protocol failed
on every upstream PR. It now requires the stamp and commit == HEAD, the
identity the runtime actually reports.
2026-09-23 15:53:09 -04:00
ethernet
fe894cca9d test(install): served PowerShell scripts carry no UTF-8 BOM
`& ([scriptblock]::Create((irm ...)))` is the documented Windows install
form. Windows PowerShell 5.1 irm keeps a leading BOM as a literal U+FEFF,
so param( is no longer the first statement and the script dies with "The
assignment expression is not valid". The Windows-lane ParseFile check reads
the BOM as an encoding marker and passes, so nothing caught the BOM that
came back with the MSIX rewrite of install.ps1. This check is
host-independent and runs on the Linux lane.

Red on install.ps1 until its BOM is stripped.
2026-09-23 15:46:26 -04:00
ethernet
86f42bf3b6 fix(docker): stamp a commit-only identity when no release tag is reachable
Upstream carries only CalVer tags, which version_from_tag rejects on
purpose, so every PR image build died in "Write install stamp" with
"no reachable release tag". The runtime already reads a tagless source
checkout as base "unknown" plus its commit; the image now records the
same: the workflow admits GITHUB_SHA via --commit, adds version args only
when a release is reachable, and write_install_stamp accepts a missing
base version when the caller supplied the commit (a local tree still
stays unstamped).
2026-09-23 15:44:18 -04:00
ethernet
107af642b2 feat(release): stage one receipt per build group
Stable now calls the desktop bundle workflow once per build group, and
each Mac arch and the Windows bundle assembly stage a <group>-receipt.json
into its attempt archive before the group's smoke runs. The receipts let
the next commit start each install arm from its own group's bytes.

publish-bundles and complete temporarily lose their candidate-manifest
digest source; the next commit moves that job into stable-release.yml and
wires the digest back.
2026-09-23 14:43:26 -04:00
ethernet
525ead7866 fix(release): gate the whole-build pages on one all-jobs switch
builds-pending ran whenever termux_only was false. The jobs input turned
that into 'termux is selected', so a desktop-only run skipped the pending
page and a termux-only run wrote one that builds-table never finalizes.
Admission now emits all-jobs from the same parser, and builds-pending,
builds-table and commit-builds-summary gate on it.
2026-09-23 14:15:07 -04:00
ethernet
0213af511c fix(release): write the stable mac feed during publication 2026-09-23 14:12:38 -04:00
ethernet
04ee0d7166 feat(release): select desktop build jobs per arch with one jobs input
termux_only is replaced by jobs=termux. smoke-win32-universal is removed: the per-arch MSIX smokes cover each arch, and stable's install arms install the msixbundle on both arches.

validate_receipt no longer requires smoke results: receipts are staged right after the bytes and before the smokes run (decision 11), so only the final manifest (validate_candidates) still requires every SMOKE_JOBS result.
2026-09-23 14:11:21 -04:00
ethernet
b16f953f7c fix(pm): re-activate from the shebang only when an input's mtime differs
The _hermes-python prologue compared uv.lock / pyproject.toml / pm/lock.json
against facts.json with `-nt`. facts.json is only rewritten on a real sync,
so a checkout that rewrites an input without changing it left the input
newer forever: every run from an activated shell re-sourced activate
(~370ms instead of ~13ms).

pm now records, after every successful full-closure install (no-op syncs
included), a stamp per input under installs/<key>/inputs carrying the exact
mtime the install was verified against, snapshotted before installing so
an input edited mid-install still reads as stale. The prologue re-activates
when any input's mtime differs from its stamp in either direction (branch
switches can move it backwards), when an input is missing, or when there
are no stamps. It globs the stamp dir, so pm owns the only input list.

Also read pyvenv.cfg as utf-8-sig (footgun lane, same file).
2026-09-23 13:38:54 -04:00
ethernet
8c050ce93c feat(release): hold the Store submission until publish
The green run no longer lets the Store go live on its own: stable-store
deletes any in-flight submission, uploads the verified msixbundle as a
draft (msstore publish --noCommit), sets targetPublishMode to Manual via
msstore submission get/update, and commits with msstore submission
publish. The publication pass releases it from sequencer production_advance
(after the feeds and Docker aliases move) through the Partner Center
submission REST API with the same MS_STORE_* credentials: it finds the
in-flight submission, and rewrites it with targetPublishMode Immediate and
commits, so a certified (status Release) submission goes live now and one
still in certification goes live when certification passes. Both calls act
on the submission's current state and are safe to rerun; a failed Store
call leaves the run red. The Store product id is optional in the
publication environment, so runs without Store credentials skip the step.
2026-09-23 13:29:06 -04:00
ethernet
6e64e961d8 fix(ci): better logs on bundle builds 2026-09-23 13:17:49 -04:00
ethernet
c0100f5f1c feat(release): validate one receipt without the others
Each install arm starts from its own receipt, so stable must accept a
manifest that covers only one arch or the Windows bundle. The per-row
checks move into one helper shared by validate_candidates and the new
validate_receipt, and the per-target transition logic into one helper
shared by plan_transitions and plan_receipt_transitions. The full
manifest still has to cover every desktop target.
2026-09-23 13:13:36 -04:00
ethernet
3f2e40f07d feat(release): channel records name the attempt archive
Stable archives live under the attempt ref, but channel records only
carried releaseTag, so the protected prefix check rejected the bytes the
pipeline actually writes. An optional archiveRef on the request names the
attempt archive; validators and readers use it for the releases/tag/
prefix and fall back to releaseTag, which fails closed for stable
records without it. Canary records never write the field.
2026-09-23 12:58:33 -04:00
ethernet
b37f37b692 fix(release): send the stripped draft notes as a raw string
gh api --field reads a value that starts with @ as a file and converts
true, false and integers. Generated notes can open with an @mention, so
the body goes through --raw-field like the other string fields.
2026-09-23 12:57:50 -04:00
ethernet
fcd1fdd57b feat(release): warn in the draft body against publishing it by hand
Nobody should publish a stable release from the GitHub UI, and once
immutable releases land that mistake is permanent: the release stays on
the attempt ref with no receipt tag, no feed move, and no alias move.
The draft now carries a fenced caution above and below the generated
notes, and publish strips both fences before the release goes public,
refusing an unbalanced fence. The notes are fetched from the API and
written to a file because --generate-notes cannot place text below the
notes.
2026-09-23 12:53:00 -04:00
ethernet
e64c163c8a feat(release): publish writes the receipt and retargets the release
The final tag is the custody receipt of one publication pass: publish
hashes the candidate manifest from the attempt archive (nothing records
the digest earlier), resolves the image digest from the attempt-ref tag,
and binds both with the claim and archive path into v{version}. The
draft is retargeted onto the receipt tag and stripped while it is still
a draft, and only then does a final, separate call make it public —
immutable releases take no edits afterwards. The needs_retarget recovery
reruns exactly that draft edit; a public release can never be repaired.
The stable/latest Docker aliases move onto the attempt's image in the
same pass as the feed pointers, and no bytes are copied to a v-tag path.
2026-09-23 12:42:38 -04:00
ethernet
a6e16f0934 feat(release): discover attempt refs and abandon markers
The sequencer and the release entrypoint each carried their own copy of
what makes an attempt outstanding, and discover still read the old
vX.Y.Z-rc claim shape. One pure predicate in versioning now answers it
for both, and the sequencer lists receipt tags, attempt refs, and
abandon markers, filtering each through its parser. An attempt is
burned by its marker, published by its final tag, and green only with
its draft and a succeeded workflow; more than one outstanding attempt
across all versions is refused.
2026-09-23 11:57:56 -04:00
ethernet
c13ea774e6 refactor: make install-stamp.json the single runtime version identity
Runtime identity resolved through hermes_cli.__version__ (a static 0.0.0
on source installs, rewritten by release stamping) leaked v0.0.0 into
About, /api/health, User-Agents, and plugin compat, and source updates
showed "couldn't reach update server" because identity and channel
authority disagreed with the checkout.

Now: get_version_info() resolves install stamp -> live git -> unknown,
never pyproject metadata, never a package constant. Source checkouts
derive identity from their reachable release tag; the completion tail of
every successful install/update/historical takeover atomically rewrites
install-stamp.json with that identity; a stale source stamp whose commit
no longer matches HEAD defers to live git. ACP/TUI use derived_version
for display and base_version for protocol fields; all ~44 runtime
__version__ consumers migrated; hermes_cli.__version__ and generated
_version.py are gone; release stamping only touches the native manifests
external builders consume (nix/tauri/cargo) and passes release identity
straight into write_install_stamp.py; pyproject.toml stays inert 0.0.0.
Desktop no longer synthesizes a competing install-stamp.json: the
checkout owns its stamp, and desktop-bootstrap classification keys on
the bootstrap-complete marker. verify-bootstrap-version-stamp.py now
cross-checks the checkout's stamp (baseVersion + commit == HEAD).

Validation: 31-file focused suite green (version identity, stamping,
adoption, providers, gateway, acp/tui runtime identity, api server via
extras env, release graph); desktop tsc + 25 vitest green; real-repo
probe: base=unknown derived=git.0635606.dirty source=git on this
checkout; clean-env imports resolve entirely from this tree; windows
footgun + compat-pointer scans clean.
2026-09-23 11:41:01 -04:00
ethernet
7547d72763 feat(release): write the stable archive under the attempt ref
The archive readers already keyed every object on releases/tag/<attempt>;
the writers still keyed them on the plain payload tag, so a stable run
wrote releases/tag/vX.Y.Z/ while publish read releases/tag/rc.N-vX.Y.Z/
and found nothing. Derive one archive ref at admission (validate emits
archive-tag, jobs export HERMES_ARCHIVE_TAG) and use it at every writer
and reader of a releases/tag/<x>/ key in the stable path. The plain
vX.Y.Z keeps naming the payload identity: build stamps, package
versions, feed versions, and GitHub release bodies. Canary and channel
builds keep archive == payload tag, so their keys are unchanged.
2026-09-23 10:51:01 -04:00
ethernet
f4a38b1ee8 Merge origin/main into ethie/pm-clean 2026-09-23 10:13:26 -04:00
ethernet
b56314aa40 feat(release): warn that attempt builds are not upgrade-safe
Hand-installed attempt builds share the plain package version with the
published release, so the updater never replaces them. The diagnostic
page for an attempt ref now says so above the file table (decision 25).
2026-09-23 09:58:33 -04:00
ethernet
40747f5004 feat(release): key the stable APT pool by the attempt ref
The pool upload is immutable with a one-year cache header, so a recut
of the same version must not reuse a pool key. The candidate phase now
prefixes the pool path (and the Packages Filename field) with the
attempt ref; without --pool-subdir the layout is unchanged.
2026-09-23 09:58:33 -04:00
ethernet
8e0a256f49 feat(release): key the stable archive by the attempt ref
desktop preparation parses its claim ref and carries the attempt ref as
archive_tag beside the plain payload version. Stable admission accepts
only attempt refs and returns them; accepted candidates, bootstrap and
advance_stable read the attempt-scoped archive, and docker manifests may
carry the attempt-ref image tag while stable/latest aliases stay put
until publish.
2026-09-23 09:56:49 -04:00
ethernet
6f8d7dd3f7 feat(release): the green workflow no longer writes the final tag
The final tag is a publication custody receipt, so complete() only
validates the accepted candidate archive and the draft stays on the
attempt ref. A succeeded run with its draft is green; a missing draft
after success is an error, not an inferred abandonment.
2026-09-23 09:40:45 -04:00
teknium1
0f1544f903 fix(desktop): report a failed gateway restart as a manual step, not a failed update
The salvaged restart (#119815) exited 9 when `hermes gateway start --all`
failed, which makes the hand-off's finally block write ok:false, show the
error finale and log "detached update FAILED" in the Desktop even though
the code, venv and Desktop build all verified. Move the restart after the
outcome is settled and surface a restart failure through Write-Result's
existing manual flag instead: the Desktop's boot dialog shows the
`hermes gateway start --all` follow-up, and the update stays a success.
Publish a UI stage so the marquee names the step.

Extend the salvaged test with the exit-code invariant (red on the PR's
own head) and correct the update_cmd_windows docstring that said the
Desktop never restarts the messaging gateway.

Closes #119809
2026-09-23 06:05:29 -07:00
KoNit-K
d10f1735bc fix(desktop): restart gateways after Windows update 2026-09-23 06:05:29 -07:00
ethernet
02f1364383 fix: reconcile Termux TUI and platform tests 2026-09-23 08:20:01 -04:00
ethernet
890bbbda1f Merge remote-tracking branch 'origin/main' into ethie/pm-clean
# Conflicts:
#	apps/desktop/e2e/archived-hidden-session-recoverable.spec.ts
#	apps/desktop/e2e/bot-chat-message-agent-friendly-name.spec.ts
#	apps/desktop/e2e/bot-mailbox-unreadable-ticket.spec.ts
#	apps/desktop/e2e/bot-mode-roster-localized.spec.ts
#	apps/desktop/e2e/bot-mode-row-click-mirrors-registry.spec.ts
#	apps/desktop/e2e/bot-mode-tab-shows-bot-name.spec.ts
#	apps/desktop/e2e/bot-roster-group-row-organisation.spec.ts
#	apps/desktop/e2e/bot-roster-ignores-infra-dirs.spec.ts
#	apps/desktop/e2e/bot-roster-timestamp-meta.spec.ts
#	apps/desktop/e2e/bot-roster-user-sections.spec.ts
#	apps/desktop/e2e/bot-routines-pane-narrow.spec.ts
#	apps/desktop/e2e/bot-row-open-recent-session.spec.ts
#	apps/desktop/e2e/bot-tile-ignores-ambient-composer-model.spec.ts
#	apps/desktop/e2e/group-composer-auto-grow.spec.ts
#	apps/desktop/e2e/group-create-gate-remote-roster.spec.ts
#	apps/desktop/e2e/group-prompt-renamed-primary-handle.spec.ts
#	apps/desktop/e2e/hosted-room-backend-continuity.spec.ts
#	apps/desktop/e2e/hosted-room-legacy-store-migration.spec.ts
#	apps/desktop/e2e/settings-scope-chips-bot-title.spec.ts
#	apps/desktop/e2e/worktree-branch-status.spec.ts
#	apps/desktop/electron/backend-probes.test.ts
#	apps/desktop/electron/connection-apply.test.ts
#	apps/desktop/electron/desktop-electron-pin.test.ts
#	apps/desktop/electron/desktop-uninstall.test.ts
#	apps/desktop/electron/gateway-file-download-transport.test.ts
#	apps/desktop/electron/gateway-stop-before-update.test.ts
#	apps/desktop/electron/github-api-auth.test.ts
#	apps/desktop/electron/registry-primary-profile-scope.test.ts
#	apps/desktop/electron/update-api-check.test.ts
#	apps/desktop/electron/update-handoff-marker.test.ts
#	apps/desktop/electron/venv-blocker-scan.test.ts
#	apps/desktop/scripts/after-extract.test.mjs
#	apps/desktop/scripts/local-pack-publish.test.mjs
#	apps/desktop/scripts/tasks-scroll.test.mjs
#	apps/desktop/src/app/settings/model-settings.test.tsx
#	apps/desktop/src/app/updates-overlay.blockers.test.tsx
#	apps/desktop/src/components/desktop-install-overlay.test.tsx
#	apps/desktop/src/lib/update-copy.test.ts
#	scripts/ci/check_os_marker_fakes.py
#	tests-js/desktop-mac-usage-descriptions.test.ts
#	tests-js/node-engine-alignment.test.ts
#	tests/agent/lsp/test_install_and_lint_fixes.py
#	tests/agent/test_command_token_source.py
#	tests/agent/test_compression_boundary_hook.py
#	tests/agent/test_create_openai_client_ssl_verify.py
#	tests/agent/test_custom_provider_ca_probes.py
#	tests/agent/test_endpoint_blackhole.py
#	tests/agent/test_estimator_parity.py
#	tests/agent/test_in_place_compaction.py
#	tests/agent/test_moa_loop_mode.py
#	tests/agent/test_model_metadata.py
#	tests/agent/test_skill_session_platform_gate.py
#	tests/agent/test_skill_utils.py
#	tests/agent/test_ssl_ca_guard.py
#	tests/computer_use/test_doctor.py
#	tests/cron/test_codex_execution_paths.py
#	tests/cron/test_cron_bot_chat_delivery.py
#	tests/cron/test_cron_script.py
#	tests/cron/test_media_delivery_parity.py
#	tests/cron/test_misfire_catchup.py
#	tests/cron/test_parallel_pool.py
#	tests/cron/test_recurring_eagain_redispatch.py
#	tests/gateway/test_choice_picker.py
#	tests/gateway/test_control_socket_windows_live.py
#	tests/gateway/test_dingtalk.py
#	tests/gateway/test_feishu.py
#	tests/gateway/test_feishu_onboard.py
#	tests/gateway/test_gateway_shutdown.py
#	tests/gateway/test_matrix.py
#	tests/gateway/test_model_command_custom_providers.py
#	tests/gateway/test_reasoning_command.py
#	tests/gateway/test_runtime_footer.py
#	tests/gateway/test_session.py
#	tests/gateway/test_session_hygiene.py
#	tests/gateway/test_status.py
#	tests/gateway/test_teams.py
#	tests/gateway/test_turn_lease.py
#	tests/gateway/test_whatsapp_connect.py
#	tests/hermes_cli/test_approvals_command.py
#	tests/hermes_cli/test_auth_store_lock_concurrent.py
#	tests/hermes_cli/test_backup.py
#	tests/hermes_cli/test_banner_git_state.py
#	tests/hermes_cli/test_certifi_repair.py
#	tests/hermes_cli/test_cmd_update.py
#	tests/hermes_cli/test_compat_manifest_targets.py
#	tests/hermes_cli/test_computer_use_cli.py
#	tests/hermes_cli/test_cpr_local_leak.py
#	tests/hermes_cli/test_dashboard_auth_gate.py
#	tests/hermes_cli/test_dashboard_procs_kill_grace.py
#	tests/hermes_cli/test_desktop_lifecycle_windows_live.py
#	tests/hermes_cli/test_doctor.py
#	tests/hermes_cli/test_doctor_command_install.py
#	tests/hermes_cli/test_fleet_config_migration_windows_live.py
#	tests/hermes_cli/test_gateway.py
#	tests/hermes_cli/test_gateway_platform_gating.py
#	tests/hermes_cli/test_gateway_restart_loop.py
#	tests/hermes_cli/test_gateway_task_probe.py
#	tests/hermes_cli/test_gateway_wsl.py
#	tests/hermes_cli/test_gui_command.py
#	tests/hermes_cli/test_install_cua_driver.py
#	tests/hermes_cli/test_kanban_db.py
#	tests/hermes_cli/test_lazy_command_exports.py
#	tests/hermes_cli/test_lazy_refresh_venv_repair.py
#	tests/hermes_cli/test_linux_desktop_entry.py
#	tests/hermes_cli/test_local_runtime.py
#	tests/hermes_cli/test_local_runtime_updates.py
#	tests/hermes_cli/test_managed_uv.py
#	tests/hermes_cli/test_mcp_reload_confirm_gate.py
#	tests/hermes_cli/test_nous_subscription.py
#	tests/hermes_cli/test_npm_engine.py
#	tests/hermes_cli/test_personality_none.py
#	tests/hermes_cli/test_pet_toggle.py
#	tests/hermes_cli/test_plan_reconciliation_windows_live.py
#	tests/hermes_cli/test_plugin_event_bus.py
#	tests/hermes_cli/test_plugin_manifest_v2.py
#	tests/hermes_cli/test_plugin_packs.py
#	tests/hermes_cli/test_plugins_cmd.py
#	tests/hermes_cli/test_plugins_cmd_enable_disable_nested.py
#	tests/hermes_cli/test_process_identity.py
#	tests/hermes_cli/test_profiles.py
#	tests/hermes_cli/test_profiles_sidebar_cache.py
#	tests/hermes_cli/test_pty_bridge.py
#	tests/hermes_cli/test_resolve_turn_limit.py
#	tests/hermes_cli/test_serve_runtime_inventory.py
#	tests/hermes_cli/test_session_vacuum_config.py
#	tests/hermes_cli/test_set_config_value.py
#	tests/hermes_cli/test_signal_handler_kanban_worker.py
#	tests/hermes_cli/test_slash_confirm_windows.py
#	tests/hermes_cli/test_stale_pid_guard.py
#	tests/hermes_cli/test_startup_fast_guards.py
#	tests/hermes_cli/test_status.py
#	tests/hermes_cli/test_telegram_managed_bot.py
#	tests/hermes_cli/test_tools_config.py
#	tests/hermes_cli/test_update_apply_shallow_count.py
#	tests/hermes_cli/test_update_autostash.py
#	tests/hermes_cli/test_update_concurrent_quarantine.py
#	tests/hermes_cli/test_update_fetch_failure_classifier.py
#	tests/hermes_cli/test_update_fleet_probe_resume_token.py
#	tests/hermes_cli/test_update_handoff_backend_reap.py
#	tests/hermes_cli/test_update_handoff_desktop_rebuild.py
#	tests/hermes_cli/test_update_head_moved_gate.py
#	tests/hermes_cli/test_update_host_obligation.py
#	tests/hermes_cli/test_update_import_guard.py
#	tests/hermes_cli/test_update_interrupted_recovery.py
#	tests/hermes_cli/test_update_inventory.py
#	tests/hermes_cli/test_update_launchd_unloaded_gateway.py
#	tests/hermes_cli/test_update_missing_configured_deps.py
#	tests/hermes_cli/test_update_modified_notice.py
#	tests/hermes_cli/test_update_multiplex_migration_hook.py
#	tests/hermes_cli/test_update_no_gateway_restart.py
#	tests/hermes_cli/test_update_orphan_backend_reap.py
#	tests/hermes_cli/test_update_parked_branch_guard.py
#	tests/hermes_cli/test_update_post_pull_syntax_guard.py
#	tests/hermes_cli/test_update_receipt.py
#	tests/hermes_cli/test_update_self_lock.py
#	tests/hermes_cli/test_update_shim_fail_closed.py
#	tests/hermes_cli/test_update_shim_self_lock.py
#	tests/hermes_cli/test_update_sqlite_remediation.py
#	tests/hermes_cli/test_update_stale_dashboard.py
#	tests/hermes_cli/test_update_stale_virtualenv.py
#	tests/hermes_cli/test_update_venv_health.py
#	tests/hermes_cli/test_update_venv_ownership_preflight.py
#	tests/hermes_cli/test_update_wedged_gateway.py
#	tests/hermes_cli/test_update_yes_flag.py
#	tests/hermes_cli/test_update_zip_two_phase.py
#	tests/hermes_cli/test_urllib_security.py
#	tests/hermes_cli/test_ux_messages_auth_config.py
#	tests/hermes_cli/test_ux_messages_startup.py
#	tests/hermes_cli/test_venv_holder_classifier.py
#	tests/hermes_cli/test_verify_console_scripts.py
#	tests/hermes_cli/test_verify_core_dependencies.py
#	tests/hermes_cli/test_web_server.py
#	tests/hermes_cli/test_web_server_console_ws.py
#	tests/hermes_cli/test_web_server_ws_ping.py
#	tests/hermes_cli/test_web_ui_build.py
#	tests/hermes_state/test_fts_rebuild_admission.py
#	tests/hermes_state/test_hermes_state.py
#	tests/plugins/memory/test_memory_lazy_install.py
#	tests/plugins/test_google_meet_plugin.py
#	tests/plugins/test_langfuse_plugin.py
#	tests/plugins/test_security_guidance_plugin.py
#	tests/plugins/test_transform_llm_output_hook.py
#	tests/scripts/desktop_update/test_desktop_update_windows_gateway_flag.py
#	tests/scripts/desktop_update/test_desktop_update_windows_python_handoff.py
#	tests/scripts/desktop_update/test_desktop_update_windows_timestamp.py
#	tests/scripts/install/test_install_clone_throttle_fallback.py
#	tests/scripts/install/test_install_lockfile_churn.py
#	tests/scripts/install/test_install_no_initial_commit.py
#	tests/scripts/install/test_install_sh_browser_install.py
#	tests/scripts/install/test_install_sh_node_prerelease.py
#	tests/scripts/install/test_install_sh_symlink_stomp.py
#	tests/scripts/install/test_install_sh_uv_lock_config.py
#	tests/scripts/install/test_install_unmerged_index.py
#	tests/scripts/test_contributor_map.py
#	tests/scripts/test_run_tests_parallel.py
#	tests/skills/test_competitor_news_monitor_skill.py
#	tests/skills/test_document_to_action_items_skill.py
#	tests/skills/test_google_workspace_setup.py
#	tests/skills/test_google_workspace_setup_deps.py
#	tests/skills/test_grounded_citations_skill.py
#	tests/skills/test_ip_as_logo_skill.py
#	tests/skills/test_live_dashboard_skill.py
#	tests/skills/test_mcp_oauth_remote_gateway_skill.py
#	tests/skills/test_office_document_skills.py
#	tests/skills/test_openclaw_migration.py
#	tests/skills/test_product_price_monitor_skill.py
#	tests/skills/test_scrollcraft_skill.py
#	tests/skills/test_setup_wizard_generator_skill.py
#	tests/skills/test_weekly_review_planning_skill.py
#	tests/test_engines_satisfiable.py
#	tests/test_fast_safe_load.py
#	tests/test_hermes_bootstrap.py
#	tests/test_hermes_constants.py
#	tests/test_hermes_logging.py
#	tests/test_managed_runtime_resolution.py
#	tests/test_model_tools_async_bridge.py
#	tests/test_packaging_build_guard.py
#	tests/test_packaging_metadata.py
#	tests/test_yaml_indent_consistency.py
#	tests/tools/test_approval_timeout_overflow.py
#	tests/tools/test_base_environment.py
#	tests/tools/test_bot_mode_dm.py
#	tests/tools/test_browser_chromium_check.py
#	tests/tools/test_browser_hardening.py
#	tests/tools/test_browser_homebrew_paths.py
#	tests/tools/test_browser_npx_warmup.py
#	tests/tools/test_browser_orphan_reaper.py
#	tests/tools/test_browser_real_profile.py
#	tests/tools/test_browser_use_cli.py
#	tests/tools/test_clipboard.py
#	tests/tools/test_code_execution.py
#	tests/tools/test_code_execution_modes.py
#	tests/tools/test_code_execution_windows_env.py
#	tests/tools/test_computer_use.py
#	tests/tools/test_delegate_liveness_timeout.py
#	tests/tools/test_execute_code_approval_cluster.py
#	tests/tools/test_execution_flag_detection.py
#	tests/tools/test_fal_common.py
#	tests/tools/test_file_operations.py
#	tests/tools/test_file_tools.py
#	tests/tools/test_file_tools_cwd_resolution.py
#	tests/tools/test_file_tools_live.py
#	tests/tools/test_lazy_deps.py
#	tests/tools/test_lazy_deps_durable_target.py
#	tests/tools/test_lazy_deps_managed.py
#	tests/tools/test_local_env_blocklist.py
#	tests/tools/test_local_tempdir.py
#	tests/tools/test_macos_protected_search.py
#	tests/tools/test_mcp_npx_cached_bin.py
#	tests/tools/test_oneshot_completion_linger.py
#	tests/tools/test_process_registry.py
#	tests/tools/test_read_file_schema_gating.py
#	tests/tools/test_skill_improvements.py
#	tests/tools/test_skills_sync.py
#	tests/tools/test_termux_api_detection.py
#	tests/tools/test_tirith_security.py
#	tests/tools/test_transcription_tools.py
#	tests/tools/test_tts_streaming.py
#	tests/tools/test_wake_word.py
#	tests/tui_gateway/test_compute_host_borrowed_lease.py
#	tests/tui_gateway/test_compute_host_turn_protocol.py
#	tests/tui_gateway/test_isolated_orphan_activity.py
#	tests/tui_gateway/test_protocol.py
#	tests/tui_gateway/test_slash_worker_profile_home.py
#	tests/tui_gateway/test_subprocess_encoding.py
#	tests/tui_gateway/test_tui_gateway_server.py
#	ui-tui/src/__tests__/terminalParity.test.ts
#	ui-tui/src/__tests__/termuxComposerLayout.test.ts
#	ui-tui/src/__tests__/textInputFastEcho.test.ts
2026-09-23 07:02:44 -04:00
teknium1
524c38a98a test: purge low-value tests, lane py16 (624 removed)
Change-detectors, tautologies, source-reading tests, redundant duplicates,
mock-echo tests and dead/unrunnable tests. Per-test rationale in the lane
ledger (category + reason for every removal).
2026-09-23 03:15:26 -07:00
alt-glitch
8d34601f47 fix(release): stamping a payload snapshot skips the bootstrap-installer check
The payload snapshot omits apps/ (INERT_SNAPSHOT_DIRS), so validate_bootstrap_version
raised FileNotFoundError on every native bundle leg since dd72571178.

(cherry picked from commit 8411fdb333)
2026-09-23 02:00:32 -04:00
ethernet
60c023c7af feat(release): admit rc.N-vX.Y.Z claim refs
Admission parses the attempt ref for the version and the attempt, and the
claim metadata must name the same attempt. release has written "attempt"
into every claim since the attempt refs landed; without this, admission
would refuse each of them for an unexpected key.

The attempt stays out of the workflow outputs: emit writes an explicit key
list and no job reads it.
2026-09-23 00:47:16 -04:00
ethernet
949ec52a51 feat(release): abandon writes an immutable marker ref
abandon clears the outstanding attempt of a version by pushing
abandoned-rc.<N>-vX.Y.Z at the attempt's commit. The attempt ref stays, and
the next cut is rc.<N+1> of the same version.

The draft is deleted before the marker is pushed: a cleared attempt with a
live draft could still be published by hand, while a draftless outstanding
attempt is only abandoned again. abandon reads every outstanding attempt, so
it can still clear one when a concurrent cut left two. It refuses a release
that was already published.
2026-09-23 00:43:16 -04:00
ethernet
49d1a587e8 feat(release): cut rc.N attempt refs behind one global lock
A version is now spent only by publication. derive_next_version reads the
published stable head alone, and release claims the next attempt of that
version as rc.<N>-vX.Y.Z. An abandon marker clears an attempt without
freeing its number.

At most one attempt, of any version, is outstanding: an attempt ref with no
marker and no final tag on the remote. release refuses while one exists and
prints its workflow run, the abandon command, and the rerun command. The
pre-check and the push are not atomic across versions, so release re-reads
the attempts after its push and stops before the draft and the dispatch if a
concurrent cut of another version landed.

A new attempt must descend from the published stable head, read from the
stable channel with its version. Abandoned attempts put no constraint on it.

Fetch refspecs are rc.* and abandoned-rc.*: a refspec may hold one '*', and
the parsers filter what the globs over-match.
2026-09-23 00:39:20 -04:00
ethernet
2fa376bbec feat(release): parse attempt refs and abandon marker refs
An attempt ref is rc.<N>-vX.Y.Z and its abandon marker is
abandoned-rc.<N>-vX.Y.Z. The ref grammar only anchors: version_from_tag
still owns the version shape, so CalVer and canary identities stay out.
2026-09-23 00:28:55 -04:00
alt-glitch
8411fdb333 fix(release): stamping a payload snapshot skips the bootstrap-installer check
The payload snapshot omits apps/ (INERT_SNAPSHOT_DIRS), so validate_bootstrap_version
raised FileNotFoundError on every native bundle leg since dd72571178.
2026-09-23 09:46:04 +05:30
ethernet
9e421e2fb7 test: repair release and host probe seams 2026-09-22 18:36:19 -04:00
ethernet
a9b931585b fix(release): say what each release command did and what to do next
release printed the claim URL and stopped. The other commands printed a
record or a one-line status. Each command now says what happened, what to
wait for, and the next action.

release names the claim, the workflow run, the notes page, and the publish
command when autopublish is off. publish and canary name their workflow.
abandon says the version is spent. A commit build names its page and says
it moves no channel.
2026-09-22 12:33:02 -04:00
ethernet
e64351c8e7 fix(release): support the pre-stable development graph 2026-09-22 12:19:01 -04:00
ethernet
7e9d4239c6 fix(release): preserve reachable version identity in CI 2026-09-22 12:00:06 -04:00