66 Commits

Author SHA1 Message Date
ethernet
74163fa18c fix(desktop): pass release epoch to native version subprocess 2026-09-29 10:55:18 -04:00
ethernet
b02c7f7cb1 fix(release): pass the executable path to PowerShell via the environment
powershell -Command joins trailing argv into the script text, so $args
was never populated and the path became a stray token (ParserError).
An env var needs no quoting.
2026-09-24 22:44:37 -04:00
ethernet
051febedfd fix(release): read the Windows executable name from the MSIX manifest
record() looked up stamp['identity']['windowsExecutableName'], a key no
stamp writer emits, so every Windows record on a runner resolved no
executable and failed. The built package's Application/@Executable
names the exe; match it in the unpacked dir only, which also skips
before-pack's .bak rollback copy.
2026-09-24 21:28:32 -04:00
ethernet
dc11e3b3bc feat(release): add --skip-bundles and --skip-tests to stable releases
`release.py release` gains two flags. They can be used together.

--skip-bundles ships only the claim, the GitHub release, the final tag
and the Docker image. No desktop, Termux or PM bundle job runs. The
final tag records candidateManifestSha256: null. Publication moves only
the Docker stable/latest aliases. The R2 stable head, feeds, APT, the
downloads page, the signed-package baseline and the Store stay on the
previous bundle release.

--skip-tests builds, signs and publishes every artifact and runs no
test job: source CI, Nix, PM bundle check, Termux, Windows live,
install/update E2E, bootstrap identity, native smokes, upgrade
acceptance, tests/docker and the in-build vitest step. The candidate
manifest records each smoke as skipped, never as passed.

The flags live in the claim message (skipBundles, skipTests), next to
autopublish. They are not workflow inputs, so a rerun cannot change
them. admit emits them, and every job condition and gate reads them.
stable.validate_claim and stable.validate_final are now the one shape
check for stable.py and the sequencer.

The gates stay strict. SKIPPED_BY in stable.py maps each job to the
flags that remove it. `gate` requires those jobs to report skipped and
every other gated job to report success. A job that ran although a flag
removes it blocks the release.

A release that skipped bundles never moves the R2 stable head. Two
readers depended on that head:

- The next version was derived from it, so the next cut would reuse the
  version. It now takes the newer of the R2 head and the newest
  published non-prerelease GitHub release with a vX.Y.Z tag. Bare v*
  tags do not count, because those refs are not protected yet.
- The sequencer used it to decide which published releases still need
  their publication pass, so a bundle-less release would re-advance
  every 15 minutes. The head is now the newer of the R2 head and the
  published release whose final tag binds the Docker stable alias
  digest.

`release` also refuses a cut when its next version already has a final
tag. That closes the window between the final tag and the public
release, where the published identity still names the old version.

Tests: 42 release test files, 546 passed. Three tests fail on this
Windows host, and they fail the same way on a clean HEAD worktree:

- test_stable_release_graph::test_docker_recovery_refuses_to_replace_a_divergent_version_tag
- test_release_artifacts::test_windows_metadata_is_read_from_package_and_stale_stamp_is_rejected
- test_tag_builds_summary::test_admitted_failure_publishes_tag_info_without_promoting_channel[True]

Not verified: no real Stable Release dispatch ran with either flag, and
actionlint is not installed on this host. The workflow changes are
checked by the graph tests and by running the phase-result step script.
2026-09-24 13:31:33 -04:00
ethernet
dcd2bca06a fix(desktop): render icons with core runtime dependencies 2026-09-24 01:30:15 -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
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
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
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
dcd678d6fd fix(release): close publication custody gaps 2026-09-22 02:19:38 -04:00
ethernet
dd72571178 feat(release): finalize stable claims into versioned builds 2026-09-21 22:39:16 -04:00
ethernet
14b1e7220b feat(release): derive canaries from stable build identity 2026-09-21 22:02:46 -04:00
ethernet
c4d2bb5575 feat(release): one native quad, the build time, for stable and canary
Windows VERSIONINFO and the MSIX package version were two derivations of
one fact, and the sideload one counted minutes since the last stable, which
overflows a 16-bit field after 45 days. Both now come from
storePackageVersionAt's year.hourOfYear.secondOfHour.0, so a later build
always sorts above an earlier one and the cap has nothing left to cap.
2026-09-21 20:28:41 -04:00
ethernet
2250b501b4 refactor(pm): rename cache_lock.py to uv_cache_prune.py
The module prunes uv caches to the lock; it takes no lock. Two importers.
2026-09-21 19:09:56 -04:00
ethernet
6a5a6a05d2 refactor(pm): rename the pm.ensure submodule to pm.install; lazy_deps back to the shim
`import pm.ensure` bound the submodule onto the package, shadowing the facade's
`pm.ensure()` function for every later caller in the process (photon's sidecar
start hit `'module' object is not callable`). The module is pm.install now; the
function keeps its name. The facade resolves through `__import__` rather than
`importlib.import_module` so a test that patches import_module globally does not
break attribute access on pm.

tools/lazy_deps.py returns to the 16-line stop_for_relaunch shim the branch wrote
(an origin/main merge had replaced it with main's 775-line implementation); the
project-metadata tests follow. update_cmd re-exports the four old_updater_deps
names the shim tests resolve through hermes_cli.update_cmd.
2026-09-18 23:27:05 -04:00
ethernet
ad53be7826 refactor(release): one canary-tag rule, imported from hermes_cli.update_channel
Five in-tree copies of the canary tag regex disagreed: darwin.py demanded 14 digits,
release_channels/r2 prune accepted any/8, the rest 8-or-14. r2.canary_doomed_keys
compared an 8-digit cutoff against the first 8 chars of a captured \d{8}, so a 14-digit
key matched on its date prefix by accident of regex greediness; it now slices the
suffix explicitly like scripts/release.py does. scripts/termux/deb_version.py keeps
its copy on purpose (runs under bare python3 in a workflow shell) and its test pins
it to the canonical one.
2026-09-18 19:36:29 -04:00
ethernet
ee2b165e78 chore: drop merge-resolution notes, dead imports and a dead probe module
- 15 `MERGE-CHECK:` conflict-resolution comments removed from prod code (two were
  TODOs already done: the utf-8-sig sessions.json read lives in session_persistence,
  the pm-aware cron script helpers in scheduler_script).
- 49 imports the branch left unused (ruff F401, none present at the merge base,
  none inside PLUGIN-COMPAT blocks). update_cmd's frozen-surface re-exports are
  trimmed to the names tests/compat/old_updater_surface.json actually lists under
  hermes_cli.update_cmd; the rest resolve through hermes_cli.main.__getattr__.
- tools/environments/local_gitbash_probe.py: nothing imported it once _find_bash
  delegated to pm.shell().
- Three try/except wrappers around calls that cannot raise (install_truststore,
  get_hermes_home, and a duplicated except clause in supermemory).
2026-09-18 19:31:50 -04:00
ethernet
cf0bc5d5fa perf(ci): restore a uv cache that actually warms; give the electron toolchain a producer again
The CI cache barely ever restored anything useful, for four independent
reasons found in live run logs and the fork's cache store:

- `uv cache prune --ci` (setup-pm/prune) discards downloaded wheels before
  the save, so the snapshot carried only source-built wheels — the next
  run "restored" it and still cold-downloaded everything. Upstream main's
  logs showed the end state: a 209KB stub cache exact-hitting forever.
- Tool-only jobs (icons-freshness etc.) auto-saved a 1.5KB empty uv cache
  under the production exact key; caches are immutable, so the stub won
  forever and blocked real saves.
- The npm cache key had no restore-keys, so one lockfile bump missed the
  exact key and every npm job went cold.
- `2efa4ff94f` deleted the electron-builder toolchain save but left the
  assemble job restoring `eb2-` — a fossil nobody regenerates; assembly
  cold-downloads winCodeSign/ATS/dotnet on every release.

Changes:

- pm/cache_lock.py: move prune_uv_cache_to_lock out of
  scripts/bundles/native.py (which re-exports it); the lock-exactness
  contract now serves both the bundle ship gate and CI caches.
- pm.build_env learns --exact-lock --lock-source: prune cache entries the
  project uv.lock cannot resolve, keeping lock-required downloaded wheels
  (unlike --ci). Refuses --ci/--prune-cache combinations.
- setup-pm: python-cache auto-save now requires extras (no stubs from
  tool-only jobs); key drops the prune flag and bumps to v3 — pruned and
  unpruned saves share one namespace since both are lock-exact; pre-save
  pruning switched from --ci to --exact-lock; npm cache gains a
  lockfile-agnostic restore prefix.
- save-pm-cache: same exact-lock prune before explicit saves.
- desktop-bundled-release: build legs (cache-mode: write) restore+save the
  electron-builder toolchain under eb3- keyed on the locked builder
  version; assemble restores the same namespace; the dead default-cache
  resolution step is removed (assembly resolves no electron artifacts).
- cleanup_pm_toolchain_caches.py: match v2 and v3 smoke keys.

Validation: tests/scripts/test_bundle_native.py 11/11 (incl. both
lock-prune gates), tests/pm failures identical before/after the diff,
tests/scripts/test_bundle_payload.py 5/5, tests/ci cleanup 3/3,
tests-js setup-pm-post 1/1 and the three setup-pm-cache contract tests
updated and green (6 failures in that file pre-date this diff and are
drift between the workflow and its stale assertions); pm.build_env
--exact-lock E2E against a real uv cache copy pruned 3 stale entries and
kept the rest.
2026-09-15 12:56:39 -04:00
ethie
f07d00ebb5 perf(bundles): prune the shipped uv cache to the lock — the ship gate for rolling CI caches
The CI desktop-build cache restores by prefix after dependency changes, so
it accumulates wheels for superseded pins; measured 210MB of sediment on a
real build host. stage_uv_cache copied that snapshot wholesale — stale
wheels shipped in every bundle and grew monotonically.

prune_uv_cache_to_lock now deletes archive buckets and wheel/sdist index
entries the shipped uv.lock cannot resolve, after staging. The lock is the
ship contract: CI transport may roll and accumulate; the payload never
ships sediment. Unidentifiable buckets (no dist-info) survive — pruning
fails open for unknown layouts, never for identifiable stale pins.
2026-09-14 21:45:49 -04:00
ethie
4f3b8d713e perf(bundles): ship the full uv cache; offline per-install rebuilds resolve from shipped wheels
Un-slim the bundle's uv cache: prune_uv_cache_to_built (built-only keep-set)
is deleted — every wheel the build resolved ships, so any per-install venv
rebuild (plugin extras, feature changes) resolves offline with zero network.

stage_uv_cache keeps excluding sdist src/ trees (build-only bulk) but now
KEEPS built wheel ZIPs including the copies beside sdist metadata.msgpack
shards: probing showed an offline rebuild whose revision entry lacks its
wheel ZIP fails closed wanting the sdist download. Fixture sdist uses a
stdlib-only PEP 517 backend so the hermetic offline rebuild needs no build
tools.
2026-09-14 21:01:50 -04:00
ethernet
f6713f2762 perf(bundles): bake payload bytecode; ship only what the sealed runtime reads
First launch of a bundled payload paid a cold-compile stall: the launcher
redirects bytecode writes to a user-level cache (signature-breaking on
macOS, read-only mount on AppImage/MSIX), so every import compiled from
source. Now staging bakes the cache into the payload:

- compileall with the payload's OWN staged 3.14 interpreter, unchecked-
  hash pycs: repack mtimes cannot invalidate them, a stale source can
  never trigger a rewrite, and read-only pycs mean the macOS signature
  never observes a change. Dirs stay writable — in-place rebuilds
  rmtree the tree; asserted coverage plus unchecked-hash means no
  cache-miss write can target them.
- coverage is the perf contract: the bake FAILS if any parseable module
  lacks a pyc (empirically 0 unparseable files ship, so compileall is
  strict). Probe suite: py_compile/cache_from_source, PEP 552 flags,
  multi-root read, stale-source no-rewrite, read-only cache-dir import.
- launcher: the baked marker makes configure() leave sys.pycache_prefix
  UNSET — the prefix relocates reads too and would hide the baked pycs.
  Payload modules read their source-adjacent cache (Python's default
  multi-root lookup); plugin/user modules keep caching beside their own
  sources under HERMES_HOME. Unmarked payloads keep the old redirect.
- snapshot(): the sealed payload ships without tests/website/evals/
  .github/nix/docker/tests-js (~69MB, 46% of tracked bytes) and without
  apps/ui-tui/web/scripts — CI prebuilds those products, and
  is_bundled_payload routes sealed updates to the channel updater, so
  the rebuild graph never runs in a bundle (linux_desktop_entry degrades
  to the themed icon). Frontend product staging keeps the full tree.
- test_bundle_native now stages the FULL relocatable toolchain (a bare
  interpreter ELF falls back to its compile-time /install prefix and
  cannot create a venv), and runs on the real 3.14 for the first time
  this campaign — the whole battery had been running 3.12 against the
  3.14-pinned lock.
2026-09-14 19:26:04 -04:00
ethernet
52cf71d037 Merge branch 'banana' into ethie/pm-clean 2026-09-14 15:39:15 -04:00
ethernet
6c57f10ff1 feat(bundles): slim the shipped uv cache to self-built wheels only
The desktop payloads shipped the builder's full uv cache (2.0GB mac,
1.1GB win-x64, 800MB win-arm64) so a mutable-venv rebuild could run
offline. But the bundle contract never needed offline rebuilds — it
needs rebuilds that never invoke a compiler: packages with no
downloadable wheel (sdist-only or platform gaps) would otherwise
demand Xcode CLT/MSVC/Rust on the user's machine.

prune_uv_cache_to_built keeps only what the builder compiled itself,
detected from the cache's own records (sdists-v9 entries, cached
wheels not listed in uv.lock, archive-v0 buckets by dist-info) and
drops the ~245 packages whose wheels PyPI re-serves in seconds.

Validated live on a clean host per target:
- macos arm64 (iris, no uv): 2.0GB -> 3MB; sync online, 0 builds,
  pilk/psutil/alibabacloud-tea installed from the slim
- win11 arm64 (promise, no uv/MSVC): 800MB -> 48MB; sync online,
  0 dependency builds, all 12 CI-built packages (cryptography,
  httptools, brotlicffi, dependency-injector, ruamel-yaml-clib,
  obstore, davey, firecrawl-anydoc, pilk, psutil, alibabacloud set)
  import clean. The kept set matched the CI build log's Built list
  exactly (12/12, no false positives).

Index-dir detection is name-agnostic (pypi/ vs custom --index-url
hashed dir), proven by the rewritten bundle test: sdist-only survives
the slim, downloadable wheels are dropped, the build machine's cache
is untouched, and an online rebuild compiles nothing.
2026-09-14 15:23:31 -04:00
ethernet
31a26c5896 feat(release): shared assembly, scoped publication, harness fold (rounds 2 cont.)
Checkpoint remainder: the unstaged half of the reduction work.

- channel_artifacts.py becomes the single native manifest/feed writer;
  release_artifacts.py and channel release paths delegate to it
- r2_scope fork isolation binds namespace before credential access;
  disposable runs refuse unscoped requests
- canary bootstrap verifies canary's own promoted outputs
- retained-link inventory + empty-directory preservation in strict
  migration snapshots; connection-collision resolution and URL-credential
  rejection in retirement connection adoption
- harness controller test relocated to tests/scripts/ (canonical pytest
  name); channel-retirement install-e2e jobs wired
- S/T receiver candidates build with stable update ownership
- test fixture updates across release/source-channel suites
2026-09-14 10:26:48 -04:00
ethernet
b37acb8389 feat(release): dynamic R2-owned channels and preview retirement (rounds 1-2)
Checkpoint before round-3 reduction (two-tier retirement derived from
product identity). Includes:

- R2 channel protocol (release_channels.py, channel-protocol.ts): records,
  builds, manifests, retired channels with pinned destinationHead and
  receiverProtocol; fail-closed readers in both languages
- One shared native manifest/feed writer (scripts/bundles/channel_artifacts.py)
- Scoped/disposable R2 publication, fork isolation before credential
  access, canary bootstrap verification of its own promoted outputs
- Channel source CLI: typed SourceTarget, source-channel resolution,
  retirement downgrade refusal
- Desktop channel resolver/strategy, install-stamp/build-stamp receiver
  ownership (stable-owned S/T candidates), single-flight updater operation
- Cross-package retirement machinery (receiver/host/preservation/
  compatibility/connections/dialog/discovery, backup_migration strict
  snapshots with retained-link inventory, empty-dir preservation,
  connection-collision resolution, URL-credential rejection)
- Native install harness (tests/install/channel-retirement-*) and
  install-e2e retirement jobs
- checkout-source.test.ts transport shim now covers build_opener().open
  (was silently hitting the real network in CI)

Removed secondary certification protocol (channel_qualification.py) per
approved round-2 plan. All focused suites green at checkpoint; native
cross-package journeys unverified (to be deleted in round 3).
2026-09-14 10:26:36 -04:00
ethernet
d8f4653b52 fix(build): list dirty paths when source admission fails 2026-09-13 21:26:11 -04:00
ethernet
1df2d86262 fix(build): preserve login home for macOS signing 2026-09-13 21:02:15 -04:00
ethernet
e418f17fbb fix(build): shorten native wheel cache partitions for MSVC 2026-09-13 20:32:50 -04:00
ethernet
bf75bc2516 feat(ci): require desktop chat after bundle and install builds
Share the real composer, provider-witness and completed-reply check across
post-build bundle smoke and desktop-bearing install/update checkpoints.
Keep native automatic-relaunch proof separate from post-update chat.

Download receipt-bound artifacts without release credentials and install
DMG, ZIP, MSIX and universal MSIXBUNDLE on each native architecture.
Split Windows assembly from feed publication; publish tested bytes only.
Bind candidate smoke results into the manifest used by stable promotion.

Verify historical/source provenance without assuming a version IPC commit,
strip CI identity from source build children, and use the actual Electron
PID rather than Playwright's Windows launcher wrapper.

Validation: real Linux Electron chat and sequential OLD/NEW source smoke
with preserved history; 145 targeted Python tests and 14 JS tests passed;
TypeScript, shell/PowerShell parsing and workflow checks passed.
Native macOS/Windows deployment and historical upgrades need Actions proof.
2026-09-13 19:57:38 -04:00
ethernet
176d1c771e fix(build): decode release and Termux inputs consistently 2026-09-13 18:21:59 -04:00
ethernet
8115e08e39 fix(packaging): seal shared PM metadata without private-store modes 2026-09-13 18:18:23 -04:00
ethernet
2efa4ff94f refactor(desktop): prepare dependencies before saving build caches
Dependency acquisition during packaging left native wheels and packager
inputs outside the pre-build cache save. Compose PM and existing providers
into a preparation phase, then require builds to consume admitted inputs.

Share native preparation with PM Bundle. Keep path-bound environments and
signing outputs separate from reusable caches. Use read-only cache tokens
for commit builds and preserve the one-command local build path.

Verify pinned tools through PM, probe PTYs under the prepared Electron,
and supply dmgbuild through a build-only PM package. Resolve bundled tool
stores from their payload manifest so relocation preserves discovery.

Validation: focused Python and JS tests, checkJs, Ruff, Windows checks,
anti-slop, cache relocation, and network-denied Linux AppImage builds.
Relocated runtime smoke passed with NixOS host libraries supplied.
Native Windows/macOS signing and live GitHub cache behavior remain untested.
2026-09-13 14:28:31 -04:00
ethernet
0a224e5844 fix(pm): own the pinned Darwin archiver default 2026-09-13 13:05:54 -04:00
ethernet
7d73a180ae Merge branch 'ethie/uv-build-logs' into ethie/pm-clean 2026-09-13 11:18:46 -04:00
ethernet
9a42d60f25 fix(build): handle large release uploads and trim packaging waste 2026-09-13 11:04:03 -04:00
ethernet
690316c589 Give release Python sole ownership of App Installer descriptors 2026-09-12 19:00:39 -04:00
ethernet
53e6f001c7 refactor(pm): consolidate runtime ownership and updater completion
Run historical updater completion in a fresh interpreter so cached imports
cannot revive retired dependency installers. Share Git and ZIP completion,
carry receipt and recovery state, and preserve child exit status.

Route plugin admission, binary acquisition, desktop launch and build paths
through PM. Replace redundant helpers and tests with real worker, package,
publication and launch checks. Keep the shipped compatibility surface fixed.

Targeted Python and desktop checks pass. Native update journeys and fresh
production image qualification remain pending. This is a checkpoint before
those acceptance runs.
2026-09-12 16:30:35 -04:00
ethernet
5e4a2a3d24 refactor(pm): remove legacy dependency and launch managers
Competing installers and checkout-local venv assumptions bypassed PM
selection, install consent, and generation lifetimes. Route consumers
through PM and installation-bound launchers. Refresh source launchers
before obsolete Python entries can be collected.

Remove Node, browser, and CUA acquisition engines, obsolete venv-holder
handling, detached sync, and unused PM APIs. Keep historical updater
exports inert and preserve external tool ownership and native integration.

Share product freshness and prepared inputs across builders. Align plugin
admission, Docker provisioning, setup instructions, and behavioral tests.

Verified targeted Python and JavaScript tests, desktop and web typechecks,
scoped lint, real product builds, and the Docker frontend smoke test.
The missed post-setup test cleanup is included and verified.

Native Windows/macOS execution, full Rust compilation, and the complete
repository suite remain unverified. Historical compatibility requirements
were preserved and extended, not fully rescanned.
2026-09-12 14:57:38 -04:00
ethernet
cb17e9ba72 fix(build): allow more time for native wheel builds
Give native payload dependency builds two hours without changing the normal install timeout. Allow three hours for the standalone PM bundle job so setup, cache saves, and smoke tests fit around the build.

Verified seven focused tests, Ruff, and actionlint. Full CI builds were not rerun.
2026-09-12 13:20:02 -04:00
ethernet
2a0b69858d fix(build): reuse the shared PM cache during payload staging
Resolve the default cache before isolating HOME so payload builds use the directory that CI restores and saves. Remove the standalone bundle workflow dependency on an unset cache variable.

Verified offline wheel reuse in isolated children, 20 focused tests, Ruff, and actionlint. Full native release builds and the separate source-build timeout remain unverified.
2026-09-12 13:11:36 -04:00
ethernet
6380a4e0ab fix(build): reuse cached desktop npm dependencies 2026-09-12 11:53:49 -04:00
ethernet
febb908bd1 fix(build): preserve toolchain state and Termux assembly ownership
Reuse a matching ARM64 Visual Studio environment instead of growing its paths on each initialization. Preserve Rust and Cargo homes before isolating bundle state. Hand the private Termux assembly directory to its container user and restore host ownership afterward.

Verified targeted tests, real ARM64 SDK/OpenSSL execution, Rust compilation, and container facts publication. Full release packages were not rebuilt. The Windows x64 source-build timeout and the unchanged Termux linkage fixture failure remain unresolved.
2026-09-12 02:05:48 -04:00
ethernet
d4e5ecce59 fix(bundle): reject nonstable Store builds before staging 2026-09-11 20:15:44 -04:00
ethernet
a154b89b9f Route build and CI Python preparation through PM operations 2026-09-11 18:14:43 -04:00
ethernet
7d326adf9d feat(release): bake explicit environment defaults into commit bundles 2026-09-11 14:25:12 -04:00
ethernet
fea2858c99 merge: unify shared product builders, caches, and Windows prerequisites
Merge ethie/shared-product-builders with the CI dependency cache and native Windows setup work. Preserve UTF-8 diagnostics in the shared Python environment runner. Pass a persistent cache through isolated native staging and PM-runtime construction. Reuse one Windows prerequisite installer from source setup, native adapters, and CI, preserving Rust homes across HOME isolation.

Verified 85 targeted Python tests (5 host skips), 18 JavaScript tests, workflow validation, and scoped lint/typecheck. On native Windows ARM64, five prerequisite contracts passed and the actual shared provider reused OpenSSL, compiled its header with MSVC, and retained Rust under isolated HOME. Full signed distribution builds and live Actions cache transfer remain CI verification.
2026-09-11 13:45:05 -04:00
ethernet
0a3a189235 refactor(build): remove superseded launcher templates 2026-09-11 13:17:10 -04:00
ethernet
1bf588234c refactor(build): share product recipes across distributions
Build TUI, web, desktop UI and runnable agent products from explicit
prepared inputs. Keep dependency preparation separate from distribution
packaging, with PM and native builds sharing uv environment construction.

Docker copies compiled frontend products instead of build dependencies.
Nix retains uv2nix environments and consumes shared assembly through store
references. Native desktop and Termux use the same launcher and frontend
contracts. Preserve the independent PM runtime and source imports from
arbitrary working directories.

Keep failed frontend builds from replacing the previous product, reject
source/output overlap, and bound dependency-process output draining.
Include hermes_wisdom in the Nix wheel: real CLI smoke tests exposed its
missing package declaration on the base revision too.

Verified focused Python and JavaScript suites, Docker build/runtime checks,
Nix desktop and CLI/ACP checks, standalone TUI and packaged Electron PTY,
and real full-Chromium interaction. Native signed installers, Android device
installation and the full repository suite remain CI verification.
2026-09-11 13:16:55 -04:00
ethernet
284dbaf537 fix(pm): isolate bootstrap dependencies and unify YAML on ruamel
Activation reaches plugin discovery before the application dependencies
exist. Give PM its own locked Python project and runtime so it can install
or repair the application without importing that dependency tree.

Keep PM outside the application workspace. A shared uv workspace resolves
the application graph and cannot provide this isolation. Route mutations
through an isolated worker and preserve transaction callbacks, cancellation,
custom package registrations, and correlated receipts.

Use the same runtime builder for source installs and packaged payloads.
Keep offline wheelhouse support in that builder. Nix builds the independent
PM lock as a separate derivation. Refuse lazy-disabled bootstrap before
installing tools or dependencies.

Move first-party YAML readers and writers to ruamel. Keep the application
lock's transitive PyYAML requirements for third-party packages.

Verification:
- Focused canonical Python suite: 177 passed, 1 host-gated skip.
- Electron backend probes: 12 passed. Electron typecheck passed.
- Both uv locks, scoped lint, Bash syntax, and whitespace checks passed.
- Cold activation, corrupt-app repair, offline staging, and relocation ran.
- Built and exercised the Nix PM runtime and standalone YAML merge script.

Six broader caller test files retain the same 24 failing test IDs as an
archive of HEAD. The existing real-home guard blocks those tests before
they can exercise the affected paths. No full-suite pass is claimed.
Native Windows signing and full Bionic package execution remain unverified.
2026-09-11 12:23:51 -04:00