425 lines
24 KiB
Markdown
425 lines
24 KiB
Markdown
# Shared bundle builds
|
||
|
||
Hermes separates dependency preparation, product builds, and distribution
|
||
packaging. The compiler and agent-assembly interfaces live in
|
||
[`scripts/build/README.md`](https://github.com/NousResearch/hermes-agent/blob/main/scripts/build/README.md). These are current
|
||
interfaces, not proof that every distribution passed native acceptance.
|
||
|
||
## Providers, products, and distributions
|
||
|
||
| Layer | Responsibility | Implementation |
|
||
|---|---|---|
|
||
| Dependency providers | Prepare tools, Python environments, JavaScript dependencies, and native bindings | PM (`pm.build_environment`), `scripts/build/node-deps.mjs`, Nix, and Termux |
|
||
| Product builders | Compile icons/TUI/web/desktop UI or assemble a runnable agent from prepared inputs | `scripts/generate_icons.py` and `scripts/build/` |
|
||
| Distribution adapters | Select products and package them for their target | `scripts/bundles/`, `Dockerfile`, `nix/`, and `scripts/termux/` |
|
||
|
||
Providers retain their package managers and locks. Product builders do not
|
||
install missing dependencies or download inputs. The Node dependency provider
|
||
uses one locked workspace union. Nix supplies its own npm and Python inputs.
|
||
Termux supplies bionic tools and wheels. Shared recipes do not imply identical
|
||
bytes, dependency selections, or Python environments across targets.
|
||
|
||
| Product | Implementation | Inputs |
|
||
|---|---|---|
|
||
| Icons | `scripts/generate_icons.py` | Artwork and a Python with the runtime dependencies |
|
||
| TUI | `scripts/build/tui.mjs` | Prepared TUI workspace |
|
||
| Dashboard | `scripts/build/web.mjs` | Prepared web workspace and generated icons |
|
||
| Desktop UI | `scripts/build/desktop.mjs` | Prepared desktop workspace, icons, stamp, and native bindings |
|
||
| Runnable agent | `scripts/build/agent.py` | Code, interpreter, dependencies, independent PM runtime, resources, and selected frontends |
|
||
|
||
The desktop UI compiler does not depend on the dashboard. A bundled desktop
|
||
selects an agent with TUI and dashboard products. A light desktop does not
|
||
select an agent payload.
|
||
|
||
## Build and packaging entrypoints
|
||
|
||
Source updates and source UI launches use the same dependency provider and
|
||
product builders. `hermes_cli/source_build.py` selects the workspace union,
|
||
prepares it once, then invokes the shared recipes. An update builds TUI and
|
||
web, plus the local desktop app when one was present before the update.
|
||
Desktop packaging still belongs to the local desktop adapter.
|
||
|
||
The current-checkout retry, pulled-checkout, and ZIP update paths all use
|
||
`update_cmd_maint._prepare_updated_checkout`: one PM dependency sync, then a
|
||
fresh process on the selected interpreter for the frontend builds. Build
|
||
failure aborts completion; an existing stale product is not a successful
|
||
update. There is no updater-specific npm cache, fallback install, extra
|
||
refresh, or memory-provider reinstall. PM owns the complete Python union.
|
||
|
||
Build a desktop distribution from a clean checkout at its release tag. Start
|
||
with a host Python that can run the entrypoint and Git; preparation acquires the
|
||
pinned Python, Node and npm through PM. Native compilers/SDKs remain host inputs:
|
||
|
||
```sh
|
||
python -m scripts.bundles.desktop --tag=vX.Y.Z
|
||
```
|
||
|
||
Without `--prepared`, `desktop.py` requires exactly one of `--tag` or `--commit`. Commit builds
|
||
require the full commit SHA and matching checkout HEAD. `--variant` accepts
|
||
`bundled`, `store`, or `light`, with `bundled` as the default. `--repo` selects
|
||
the checkout. Arguments after `--` go to the prepared Electron Builder wrapper;
|
||
they cannot replace the admitted target, tools, output or packaging configuration.
|
||
|
||
The driver first prepares the complete dependency set: managed tools, the locked
|
||
Node workspace union, icon environment, Electron-native bindings, packaging
|
||
utilities, and (except for light) the application and independent PM environments.
|
||
Only then does it compile products, assemble the payload, and package Electron.
|
||
MSIX metadata, signing, notarization, and package formats remain adapter work.
|
||
|
||
### Split desktop preparation and consumption
|
||
|
||
The one-command build and CI use the same preparation operation. To stop before
|
||
product compilation and packaging:
|
||
|
||
```sh
|
||
python scripts/bundles/desktop.py --tag vX.Y.Z --variant bundled --prepare-only \
|
||
--work "$PWD/.build/desktop-job" --cache "$PWD/.cache/desktop-inputs"
|
||
python scripts/bundles/desktop.py --prepared "$PWD/.build/desktop-job/prepared.json"
|
||
```
|
||
|
||
`--work` and `--cache` are separate, build-owned directories. Preparation claims
|
||
the work directory itself; do not create it beforehand. It writes `prepared.json`
|
||
last, after every provider succeeds. That file contains this job's absolute paths
|
||
and source identity, not a portable cache receipt. Reprepare after moving a
|
||
checkout, changing source or locks, or losing an input. Repeated preparation may
|
||
reuse admitted dependency bytes while rebuilding path-bound environments.
|
||
|
||
`--prepared` does not accept new tag/commit/work/cache arguments. It validates
|
||
the clean checkout and prepared inputs, then fails on stale or missing inputs
|
||
instead of installing them. Bundled and Store builds may consume the same
|
||
preparation for a stable tag; light and commit builds cannot switch to Store.
|
||
Product builds still run each time. Native staging exposes `prepare_native` and
|
||
`finish_native` to this composition; `hermes pm bundle` remains a complete
|
||
native staging command, not the desktop preparation interface.
|
||
|
||
Windows and macOS release jobs use **restore → prepare → save → build**. The
|
||
cache action derives provider paths and transports candidates; it neither
|
||
creates the preparation workdir nor declares a cache hit valid. Saves run after
|
||
successful preparation, before product compilation or signing. Failed
|
||
preparation leaves completed provider data locally but does not save an overall
|
||
snapshot. The general `setup-pm` action remains available for other workflows.
|
||
|
||
Native wheel caches use compact 128-bit partitions under `python/runtime`, keyed
|
||
by the target and observed compiler/SDK inputs. Missing identities still get a
|
||
fresh partition. uv builds source distributions inside this cache, so partition
|
||
names must leave room for nested compiler outputs below Windows `MAX_PATH`.
|
||
Keep a custom `--cache` root short as well; enabling OS long paths does not make
|
||
every native compiler long-path aware. This layout retains the shared cache
|
||
transport and all target-compatible extras, including Silk.
|
||
|
||
The strict boundary forbids dependency acquisition during consumption, not all
|
||
network access: signing, timestamping, notarization and publication retain their
|
||
online responsibilities. A network-denied unsigned native build is still needed
|
||
to prove the boundary on each release target.
|
||
|
||
Stage a native agent with both frontend products, without Electron packaging:
|
||
|
||
```sh
|
||
python -m scripts.bundles.stage --out /work/agent-payload --ref HEAD
|
||
```
|
||
|
||
This command prepares and builds TUI/web products if none are supplied. It
|
||
also accepts `--tui` and `--web` together as prepared product roots. It rejects
|
||
a selection with only one of those arguments. The standalone products have
|
||
explicit input/output contracts in the builder README.
|
||
|
||
`--ref` selects the agent source snapshot. Automatic frontend compilation uses
|
||
a temporary snapshot of that same revision. Explicit frontend products must
|
||
come from the selected revision.
|
||
|
||
`hermes pm bundle --out /work/agent-payload --ref HEAD` stages native tools,
|
||
the application environment, and the agent. That PM command does not build
|
||
frontends. `npm run payload --workspace apps/desktop` uses the stage driver
|
||
that includes them. Neither command creates an Electron installer.
|
||
|
||
### Split PM Bundle preparation and assembly
|
||
|
||
PM Bundle uses the same isolated tool bootstrap and native preparation slice,
|
||
without installing desktop workspaces, icons, Electron bindings or packagers:
|
||
|
||
```sh
|
||
python -S -B scripts/bundles/native_build.py --source "$PWD" \
|
||
--work "$PWD/.build/payload-job" --cache "$PWD/.cache/payload-inputs" \
|
||
--out "$PWD/build/agent-payload" --ref HEAD --prepare-only
|
||
python -S -B scripts/bundles/native_build.py \
|
||
--prepared "$PWD/build/agent-payload.prepared.json"
|
||
```
|
||
|
||
Use a clean checkout at the selected revision. `--ref` defaults to `HEAD`;
|
||
`--commit` accepts an exact full SHA instead. Work, cache and output must be
|
||
separate; preparation claims a previously absent work directory. Without
|
||
`--prepare-only`, the driver also assembles. `--prepared` takes no new selection
|
||
or path arguments and validates the native receipt before assembly; it never
|
||
bootstraps or repairs dependencies. The receipt and lock are output siblings
|
||
(`agent-payload.prepared.json`, `agent-payload.prepare.lock`), not shipped files.
|
||
Assembly deliberately supplies no frontend products, as with `hermes pm bundle`.
|
||
|
||
The `payload-test` producer on the existing cache action selects only `tools`,
|
||
`python/runtime` and `native`. It excludes source `node_modules`, npm caches,
|
||
icon environments and packagers. PM's managed tool bootstrap still includes Node
|
||
and npm, but does not run desktop `npm ci`. Native compiler identity and Windows
|
||
ARM64 prerequisites use the shared owner once. Cache hits always undergo provider
|
||
admission; native SDK/compiler prerequisites still belong to the host. PR jobs
|
||
only restore; saves require successful preparation on the default-branch trusted
|
||
lane with no alternate source ref. No signing or publication is added.
|
||
|
||
For Termux, tools and the wheelhouse are prerequisites:
|
||
|
||
```sh
|
||
python scripts/termux/build.py --repo /work/source --payload /work/termux-payload \
|
||
--out /work/packages --tag vX.Y.Z
|
||
```
|
||
|
||
The Termux driver accepts exactly one of `--tag` or `--commit`. It prepares
|
||
the TUI workspace, calls the shared TUI builder, and passes that product to
|
||
`build_deb.sh`. It does not add the dashboard or replace bionic preparation.
|
||
|
||
The CLI contracts are in `scripts/bundles/desktop.py`,
|
||
`scripts/bundles/stage.py`, `pm/cli.py`, and `scripts/termux/build.py`.
|
||
|
||
## Shared agent and launcher contract
|
||
|
||
`AgentInputs` supplies explicit paths and a placement mode:
|
||
|
||
- `contained` keeps runtime inputs inside the native payload.
|
||
- `fixed` retains the Docker or Termux installation prefix.
|
||
- `references` keeps installed code and dependencies in separate Nix store paths.
|
||
|
||
The assembler derives console commands from `[project.scripts]`. It copies
|
||
source-layout resources and selected frontends, or links explicit reference
|
||
inputs. It writes the completion manifest after structural assembly. Nix also
|
||
receives a command/environment map for its native wrappers.
|
||
|
||
The launcher implementation lives in `scripts/build/launchers.py`.
|
||
`launcher_wrapper.py` and `mint_launchers.py` live in that same directory.
|
||
`scripts/bundles/payload.py` owns git snapshots, PM tool facts, PM runtime
|
||
sealing, and portable link handling. It no longer owns frontend placement or
|
||
launcher generation.
|
||
|
||
The desktop stamp carries the declared launch paths. Electron consumes that
|
||
contract without payload adoption or repair. Non-bundled builds carry no
|
||
placeholder agent payload. Store packages declare `microsoft-store` as their
|
||
update mechanism. Sideload bundles declare `app-installer`.
|
||
|
||
Windows launcher minting runs the payload interpreter. POSIX launchers use
|
||
explicit interpreter, source, and dependency paths. Structural assembly does
|
||
not replace target-native launch tests. A manifest alone does not prove that
|
||
a relocated or signed artifact runs.
|
||
|
||
## Independent PM runtime
|
||
|
||
PM dependencies come from `pm/pyproject.toml` and `pm/uv.lock`, not the
|
||
application dependency graph. The application environment and `pm-runtime`
|
||
are separate inputs to agent assembly.
|
||
|
||
Native and Docker preparation use `pm.runtime_stage.stage_runtime`. Termux
|
||
uses that function with its offline wheelhouse. Nix uses the independent
|
||
`nix/pm-runtime.nix` derivation. Native sealing records the payload interpreter
|
||
and PM-only site directory in `pm-runtime.json`.
|
||
|
||
Resident PM workers run the declared interpreter with `-I -S -B`. They add
|
||
only the declared PM dependency directory before the worker script. They do
|
||
not borrow application dependencies or add PM packages to the caller's
|
||
interpreter. Docker and Nix stamps point to their declared PM runtime.
|
||
These contracts live in `pm/runtime.py:55–88,171–182` and
|
||
`scripts/bundles/payload.py:58–90`.
|
||
|
||
Ordinary native staging does not scan user plugin trees. It runs provisioning
|
||
in a child with temporary home and PM roots plus an explicit persistent build
|
||
cache. Windows ARM64 prerequisite preparation runs before HOME isolation and
|
||
uses the same provider as source setup and CI. Live PM retains plugin
|
||
admission, generation selection, and transaction state. Both paths use
|
||
`pm.environment.PythonEnvironment` for explicit uv environment construction.
|
||
|
||
## Distribution boundaries and output paths
|
||
|
||
| Distribution | Selected products | Current output layout |
|
||
|---|---|---|
|
||
| Desktop bundled/Store | Icons, TUI, web, agent, desktop UI | Products: `apps/desktop/build/products/`. Agent: `apps/desktop/build/agent-payload/`. UI: `apps/desktop/dist/`. Packages: `apps/desktop/release/` |
|
||
| Desktop light | Icons and desktop UI | `apps/desktop/dist/` and `apps/desktop/release/`, without an agent payload |
|
||
| Docker | Icons, TUI, web, agent | Agent at `/opt/hermes`, dependencies at `.venv`, PM at `pm-runtime`, generated commands at `libexec` |
|
||
| Nix TUI | TUI | `$out/lib/hermes-tui/{dist/entry.js,package.json}` |
|
||
| Nix web | Web | `$out/index.html` and assets |
|
||
| Nix agent | Agent with TUI/web references | `$out/bin`, `$out/share/hermes-agent`, `$out/ui-tui`, `manifest.json`, and `command-map.json` |
|
||
| Nix desktop | Icons and desktop UI, with the Nix agent | `$out/share/hermes-desktop` and `$out/bin/hermes-desktop` |
|
||
| Termux | TUI and agent | `OUT/hermes-agent_<version>_aarch64.deb`, installed at `$PREFIX/lib/hermes-agent/` |
|
||
|
||
Native payloads contain `hermes-agent`, `tools`, `venv`, `pm-runtime`, `bin`,
|
||
`uv-cache`, and their manifests/facts. Copied frontend assets live at
|
||
`hermes-agent/hermes_cli/tui_dist/` and `hermes-agent/hermes_cli/web_dist/`.
|
||
The TUI asset directory contains `entry.js` and module-mode package metadata.
|
||
|
||
Docker keeps its existing TUI product at `/opt/hermes/ui-tui` and web output
|
||
at `/opt/hermes/hermes_cli/web_dist`. The assembler also plants its shared TUI
|
||
asset layout beneath `hermes_cli/tui_dist`. Venv command symlinks preserve the
|
||
paths used by s6 and the privilege-drop shim.
|
||
|
||
The Docker frontend stage owns npm dependencies and compilation. The runtime
|
||
stage copies only the frontend products and the locked TypeScript package for
|
||
runtime linting. Node/npm and Photon's separately installed sidecar dependencies
|
||
remain runtime inputs. Python dependency preparation precedes the application
|
||
source copy. Actual cache-hit and layer-size claims require build evidence.
|
||
|
||
Nix retains `importNpmLock`, uv2nix, platform overrides, and store references.
|
||
Its builders run during derivation builds, not evaluation. Per-product source
|
||
filters keep frontend, Python, and resource inputs separate. Nix wrappers
|
||
consume the assembler's command map and retain their PATH and extra-Python
|
||
collision policies.
|
||
|
||
**Nix wheel policy:** `nix/python.nix:128–135` sets `HERMES_NIX_BUILD=1` only
|
||
for the Hermes derivation. `setup.py:34–72` rejects general Hermes wheel/sdist
|
||
builds. The shared assembler uses the installed Nix code without another copy.
|
||
Other providers use source-layout code and metadata, not a public Hermes wheel.
|
||
|
||
Termux retains bionic wheel compilation, offline installation, native library
|
||
paths, and its fixed prefix. Its installed root contains `app`, `tools`,
|
||
`runtime-libs`, `venv`, `pm-runtime`, `bin`, and manifests/facts. APT maintainer
|
||
hooks manage the declared CLI symlinks under `$PREFIX/bin` and refuse foreign
|
||
conflicts. They do not compile dependencies during installation.
|
||
|
||
`stage_apt_repo.py` owns repository metadata and signatures. Stable and canary
|
||
suites are `hermes-stable` and `hermes-canary`. Package files publish before
|
||
signed metadata. [Stable release admission](stable-releases.md) coordinates
|
||
acceptance and publication across distributions.
|
||
|
||
## Public artifact handoffs
|
||
|
||
The desktop release workflow downloads and installs each supported bundled
|
||
format on a fresh native runner, then runs the same composer/provider/reply
|
||
check as install-e2e. Windows universal assembly stages bytes before the smoke;
|
||
its canary feed is published separately only after both native smoke matrices
|
||
pass. macOS feed publication and stable candidate acceptance are likewise gated.
|
||
See [install and chat acceptance](https://github.com/NousResearch/hermes-agent/blob/main/tests/install/README.md#post-build-artifact-smoke)
|
||
for covered formats, checkpoint evidence and the Store/Linux/no-upload limits.
|
||
|
||
`scripts.releases.handoff fetch --public-base URL` downloads a staged artifact
|
||
without R2 credentials. Supply either `--tag TAG --commit SHA` or
|
||
`--commit-build SHA`, plus the producer `--name`, destination `--root`, and
|
||
optional `--include` selectors. This uses the same receipt identity, path,
|
||
size and SHA-256 checks as authenticated handoffs. Tagged and commit-only
|
||
archives remain separate; the command does not resolve a mutable latest feed.
|
||
|
||
Public reads require HTTPS, except for loopback fixture servers. They reject
|
||
URL credentials, unsafe paths and redirects. An incomplete or corrupt download
|
||
cannot replace an existing verified destination. Receipt files are saved only
|
||
after all selected artifacts verify. Selection of one desktop format must still
|
||
reject missing or ambiguous matches before installation.
|
||
|
||
## Cache ownership
|
||
|
||
PM binary download archives and the native uv wheel cache have different
|
||
consumers. They are not interchangeable cleanup targets.
|
||
|
||
- PM retains completed downloads until package verification and publication.
|
||
Successful installs remove their exact fetch archives. Failures retain
|
||
downloads for retry. Cleanup leaves unrelated partials alone.
|
||
- Native staging prunes obsolete entries only in its build-owned tool store.
|
||
It does not prune the user's machine-wide store.
|
||
- Native staging retains `uv-cache/` for offline mutable-environment rebuilds.
|
||
It copies extracted wheels and index/revision metadata, but excludes redundant
|
||
wheel ZIPs and cached sdist `src/` trees (including Rust `target/` outputs)
|
||
before copying. The provider cache is unchanged; native signing reaches the
|
||
extracted binaries without scanning build-only artifacts.
|
||
- PM-runtime and application builds share the provider's persistent uv cache.
|
||
CI restores/saves that cache, not the temporary build HOME. Failed builds
|
||
retain completed wheels. General PM staging uses its v2 cache namespace;
|
||
desktop preparation uses its own input snapshot namespace.
|
||
Plain `uv cache prune` removes dangling entries without discarding offline
|
||
wheel inputs. It does not remove all historical versions or enforce a size cap.
|
||
- Source builds render icons on their runtime interpreter. Desktop preparation
|
||
and product staging, which have none, prepare the locked runtime dependencies
|
||
(without the application) under the job workdir; desktop's wheel cache is
|
||
`CACHE/python/build`. That environment does not enter the shipped runtime.
|
||
- Frontend `node_modules` is a provider input, not a frontend product.
|
||
Docker's runtime TypeScript and Photon selections are separate exceptions.
|
||
|
||
The native cache behavior is in `scripts/bundles/native.py`.
|
||
Removing all caches from a payload can break offline environment reconstruction.
|
||
|
||
Desktop transport selects PM tool entries, Python download/wheel caches, npm's
|
||
content-addressed downloads, prepared workspace dependencies, and native/package
|
||
tool inputs through `scripts/ci/desktop_build_cache.py`. It does not select the
|
||
workdir, virtual environments, live user home, signing tokens, or products.
|
||
The signature-result cache is separate. Native wheel partitions depend on
|
||
observed compiler/SDK/OpenSSL identity; incomplete identity deliberately forgoes
|
||
warm reuse. This is not a fully pinned host SDK.
|
||
|
||
Cache keys are lookup hints, not authorization. Native builders have separate
|
||
release and commit execution jobs with literal `cache-mode: write` and
|
||
`cache-mode: read`, respectively. GitHub enforces these permissions on scoped
|
||
cache tokens, so commit builds cannot save caches even from their own scripts.
|
||
Both branches share their matrix, environment, and steps through YAML anchors.
|
||
The original `build-win32` and `build-darwin` IDs aggregate the branches: after
|
||
successful validation and input archiving, exactly the selected branch must
|
||
succeed and the other must be skipped. A failed, cancelled, or unexpectedly
|
||
skipped selected build fails the aggregate; it cannot permit publication.
|
||
Existing publication dependencies keep using those IDs. A skipped save or a
|
||
different key prefix alone cannot prevent a script from poisoning a writer
|
||
namespace.
|
||
Default-branch canary dispatch retains default-branch cache scope; changing a
|
||
checkout SHA does not change the workflow ref's cache scope. See GitHub's
|
||
[cache access contract](https://docs.github.com/en/actions/reference/workflows-and-actions/dependency-caching#controlling-cache-access-with-cache-mode).
|
||
|
||
## Pinned binary inputs
|
||
|
||
`python -m scripts.ci.archive_inputs` preserves every HTTPS artifact in
|
||
`pm/lock.json`, across all targets, plus the Termux runtime-library and license
|
||
pins. `pm/artifact-mirror.json` owns the public mirror location. Object keys
|
||
are `upstream/sha256/HASH`, independent of filenames and release tags.
|
||
|
||
CI requests R2 first. Only 404 permits an upstream download. It verifies the
|
||
existing SHA256 before an immutable `If-None-Match: *` upload, then downloads
|
||
and verifies the stored object. Corrupt bytes, denied access, and failed
|
||
uploads stop the build. Concurrent writers may reuse identical bytes but
|
||
cannot replace an existing object.
|
||
|
||
The all-target workflow runs on pin changes on main, manual dispatch, and as
|
||
an admitted release prerequisite. It uses runner Python before the pinned
|
||
toolchain is available. Desktop jobs depend on that prerequisite and then let PM
|
||
fetch verified inputs without archival write credentials; they do not need a
|
||
second cache-seeding lane. Other protected workflows can opt into archival
|
||
through `setup-pm`'s `archive-inputs` input. Targeted archival can seed disposable
|
||
fetch entries with `--target` and `--store`; Termux also passes `--payload` for
|
||
runtime libraries. Cache hits do not skip preservation. Desktop R2 credentials
|
||
are scoped to upload steps. Untrusted PR jobs receive no publication credentials.
|
||
|
||
Installed PM clients, bootstrap installers, and Nix pin consumers use the
|
||
primary URL followed by the public mirror if the download is unavailable.
|
||
The pinned hash remains binding; no credentials or uploads are needed by
|
||
clients. PM keeps per-source resume state and reports attempted URLs.
|
||
For Termux files already removed upstream, the CI publisher can recover the
|
||
exact bytes from the community Internet Archive after an upstream 404/410.
|
||
It never repins to the latest package.
|
||
|
||
The archive must have no expiration lifecycle rule. Release pruning does
|
||
not cover its prefix. This covers PM binary pins and Termux runtime inputs,
|
||
not unpinned apt packages, OCI images, language-package registries, or native
|
||
Electron/SDK archives independently downloaded by their build tools.
|
||
|
||
## Verification boundary
|
||
|
||
The checks below distinguish product tests from distribution acceptance.
|
||
Signed installers and Android device execution require their native runners.
|
||
|
||
The macOS dmgbuild acquisition owner is part of packaging preparation, not the
|
||
strict build. Native DMG creation, detach diagnostics, signing and notarization
|
||
must be exercised after tool-provider changes. Windows x64/ARM64 and
|
||
macOS x64/ARM64 cold/warm and signed-artifact acceptance remain separate from
|
||
Linux helper tests. Linux desktop release lanes remain disabled.
|
||
|
||
Focused helper tests cannot establish all of these requirements:
|
||
|
||
- Offline frontend compilation from prepared immutable inputs.
|
||
- Standalone TUI interaction and dashboard backend/assets behavior.
|
||
- Native Electron bindings and final signed launchers on each target.
|
||
- PM isolation after native payload relocation.
|
||
- Retained-cache offline reconstruction of a mutable application environment.
|
||
- Docker non-root CLI/TUI/web/browser behavior and every image layer's contents.
|
||
- Actual Nix package execution through reference-based wrappers.
|
||
- Fresh network-disabled Termux package installation and Android device behavior.
|
||
|
||
Use the existing acceptance checks in `tests/install/BUNDLED_UPDATES.md`,
|
||
`tests/docker/`, Nix checks, and `scripts/termux/check_deb.sh` plus
|
||
`validate_installed.py`. Container acceptance does not substitute for Android
|
||
device acceptance. No build-pass claim follows from this document.
|