Commit Graph

204 Commits

Author SHA1 Message Date
ethernet
f999b5ecd2 wip fixin stuff 2026-09-21 18:19:03 -04:00
ethernet
fc4b1958dc Merge remote-tracking branch 'origin/main' into ethie/pm-clean 2026-09-21 17:55:47 -04:00
brooklyn!
d0381cea91 fix(desktop): build and verify the Windows HUD helper without an SDK 2026-09-21 16:47:50 -05:00
ethernet
4efb36f81e merge: integrate upstream desktop features through PM preparation
Merge origin/main at 8e806ae1b2. Keep native helper compilation in
prepareDesktopNativeDependencies and keep bundling/beforePack consume-only.
Bind helper sources and headers into preparation identities and cache keys;
copy admitted executable resources beside node_modules and preserve signing
semantics in product freshness checks.

Verified desktop typecheck, focused native/packaging/UI and gateway/cache
tests, and the real Linux preparation/copy/Xvfb execution path. Incoming
upstream anti-slop findings remain unchanged; no baseline was raised.
2026-09-21 15:17:44 -04:00
brooklyn!
9ff6e071fa feat(desktop): detect clean modifier taps with passive native listeners 2026-09-21 13:44:49 -05:00
ethernet
9214174e07 fix(ci): lint legs after the main merge
- check_no_tmp_literals: resolve scratch via tempfile/os.tmpdir; the termux
  container mount point is one marked variable per script
- ruff TID251: desktop E2E fixtures may reach PM internals like tests do;
  the pm.runtime_stage ban message no longer names a module that never existed
- auth_codex: build the capped httpx stream subclass on first use so importing
  hermes_cli.auth_codex no longer forces httpx (the lazy proxy in auth_constants
  was defeated by a module-scope base class; broke lanes without httpx)
- desktop-smoke: launchApp is a parameter; the bundle-env test substitutes a
  refusing launcher instead of letting Playwright spawn a dying binary
  (3 unhandled rejections failed the tests-js lane)
2026-09-19 23:25:18 -04:00
ethernet
e1576d06a6 Merge remote-tracking branch 'origin/main' into ethie/pm-clean
Resolved toward the branch: PM provisions uv/python (main's install.ps1 uv-shim
salvage + its test and workflow steps dropped), the shim re-exec stays retired,
package.json carries no electron-builder block (afterExtract identity stamp wired
into electron-builder.config.cjs instead; after-pack.mjs keeps signing only),
Desktop workspace-deps helpers stay retired. Main's scratch-dir bootstrap
(export_scratch_tmp_env) is taken and re-run after profile resolution.
2026-09-19 22:57:07 -04:00
teknium1
0d3fe24dce docs(repo): contributor and desktop dev docs stop suggesting /tmp
Throwaway HERMES_HOME, perf JSON/cpuprofile outputs and typing-lag profiles now land in the Hermes scratch dir; the CONTRIBUTING anti-pattern mention keeps its literal with a no-tmp marker.
2026-09-19 10:44:26 -07:00
teknium1
956732d4a8 chore(evals,desktop): drop literal /tmp from eval harnesses and desktop scripts
Eval probes wrote fixtures, receipts and evidence to hard-coded /tmp paths and
two live A/B tasks literally instructed the model to work in /tmp. They now
derive locations from tempfile.gettempdir() / os.tmpdir() (env overrides kept),
usage examples use relative output names, and the desktop e2e screenshot dirs,
perf scripts and the short-session repro fixture stop naming /tmp. Also adds
the explicit encoding= the windows-footgun check wants in the touched files.
2026-09-19 10:44:26 -07:00
teknium1
f97f8d5fd5 fix(desktop): remove a half-installed get-windows dir before the workspace npm install (#90829)
An interrupted extract leaves node_modules/get-windows without package.json
and npm never revisits an existing directory, so the tree stayed broken on
every later update until a manual `npm install get-windows`. Delete such a
dir (workspace hoist or app-local copy) in _install_desktop_workspace_deps
before npm runs so the same update re-extracts it; the staging repair hint
now points at `hermes desktop --force-build` instead of the manual install.

Also drops the unused `exists` injection on findHalfInstalledGetWindowsDir
and the stale "exported for tests" note on missingGetWindowsWarning.
2026-09-19 02:32:35 -07:00
teknium1
048e7b974f fix(desktop): degrade instead of failing the build when get-windows lacks its binding or helper
The unresolvable-package case already degraded, but a half-extracted install
(#90829) that keeps package.json while losing lib/binding (win32) or the
macOS helper (darwin) still threw from stageGetWindowsInto and before-pack
rethrew, so the Desktop rebuild died on the optional dep anyway. Stage the
fail-soft JS surface without a binding on win32 (the win32-arm64 precedent),
skip staging on darwin without the helper, and treat a failing native
installer the same way; the runtime reports read_window_below as unavailable
in every one of those states. The classify gate still refuses a present
binary compiled for the wrong platform.
2026-09-19 02:32:35 -07:00
teknium1
7ee8f5528d fix(desktop): name the half-installed get-windows dir and its repair when staging degrades
Follow-up to the cherry-picked #109251 (which stops a missing optional
get-windows from killing `npm run build` on win32-x64/darwin):

- The reporter's install (#90829) was not "npm skipped an optional dep" but a
  Windows in-place update interrupted by a running Desktop/gateway
  (TAR_ENTRY_ERROR): node_modules/get-windows exists with its binding but no
  package.json, so require.resolve fails while npm never revisits the
  directory. Degrading silently would leave read_window_below dead forever.
  `findHalfInstalledGetWindowsDir` walks the same node_modules ancestors
  require.resolve does; when the dir is there the warning names it and the
  repair (`npm install get-windows --save-exact` in apps/desktop, then
  `hermes desktop --force-build`).
- Warning text lives in `missingGetWindowsWarning` (pure, exported) and the
  locator is injectable, so tests assert behaviour instead of source text.
- Tests: the old per-platform "fails when absent" cases are replaced by one
  every-platform degrade invariant and one half-install → repair-hint case
  (both red on origin/main). Docs: desktop.md build-troubleshooting note.
2026-09-19 02:32:35 -07:00
salch-cred
2a9ba25994 fix(desktop): do not fail build if get-windows is missing on any platform 2026-09-19 02:32:35 -07:00
teknium1
7009611013 fix(desktop): document the afterExtract ordering, trim tests, refresh stale hook references
Follow-up to the salvaged #106846 commit (@JoaoMarcos44):

- after-extract.mjs: spell out WHY the stamp moved (electron-builder's
  beforeCopyExtraFiles rebuilds the PE with resedit for the ELECTRONASAR
  resource; rcedit then cannot commit to that exe, deterministically —
  #105629), and why disableAsarIntegrity was not taken.
- after-extract.test.mjs: two invariants — the hook wiring (afterExtract set,
  afterPack unset, ASAR integrity still on) and the stamp target
  (electron.exe on win32, nothing on other platforms). Red on origin/main.
- set-exe-identity.mjs / scripts/install.ps1: comments still named the
  afterPack hook / after-pack.mjs.
2026-09-19 02:31:27 -07:00
joaomarcos
168e0f78ad fix(desktop): order PE stamping before ASAR integrity 2026-09-19 02:31:27 -07:00
ethernet
a6ae6ace51 Merge remote-tracking branch 'origin/main' into ethie/pm-clean
# Conflicts:
#	.github/workflows/js-tests.yml
#	agent/model_metadata.py
#	apps/desktop/electron/main.ts
#	apps/desktop/scripts/bundle-electron-main.mjs
#	apps/desktop/src/app/settings/about-settings.tsx
#	apps/desktop/src/app/settings/gateway-settings.test.tsx
#	apps/desktop/src/app/settings/gateway-settings.tsx
#	apps/desktop/src/app/updates-overlay.tsx
#	gateway/shutdown_flush.py
#	hermes_bootstrap.py
#	hermes_cli/local_runtime/binaries.py
#	hermes_cli/main.py
#	hermes_cli/managed_uv.py
#	hermes_cli/update_cmd.py
#	hermes_cli/update_cmd_deps.py
#	hermes_cli/update_cmd_fleet.py
#	hermes_cli/update_cmd_maint.py
#	hermes_cli/update_receipt.py
#	hermes_cli/update_serve_obligations.py
#	hermes_constants.py
#	tests/hermes_cli/test_doctor.py
#	tests/hermes_cli/test_managed_uv.py
#	tests/hermes_cli/test_pending_supervisor_recovery.py
#	tests/hermes_cli/test_startup_fast_guards.py
#	tests/hermes_cli/test_update_desktop_stale_warning.py
#	tests/hermes_cli/test_update_fleet_restart_pending.py
#	tests/hermes_state/test_hermes_state.py
#	tests/tools/test_tirith_security.py
#	tools/bot_relay.py
#	tools/checkpoint_manager.py
#	tools/write_approval.py
#	website/docs/getting-started/updating.md
#	website/docs/reference/environment-variables.md
2026-09-18 17:26:10 -04:00
teknium1
ed45828c8b fix: inject platform into buildCommandScreenshotMonitor instead of faking process.platform in tests
The builder takes an optional `platform` (default process.platform) so the
argv test can drive the darwin and non-darwin branches explicitly. The test
no longer redefines process.platform via Object.defineProperty.
2026-09-18 11:00:02 -07:00
teknium1
b72a2bd802 test(desktop): prove the screenshot-monitor xcrun argv on every host
The salvaged test returned early off macOS, so Linux CI never executed the
assertion. Stub process.platform to 'darwin' (execFileSync is already mocked)
so the `--sdk macosx clang` argv contract is checked wherever vitest runs, and
add the off-macOS no-op as the control case.
2026-09-18 11:00:02 -07:00
Yagna Vudathu
5bb1ded879 fix(desktop): pin macosx SDK for the screenshot-monitor native build
A bare xcrun clang inherits the host default SDK, which may be newer than
the active linker (unknown-arch .tbd stubs at link time). Naming --sdk
macosx explicitly forces the driver and linker to agree on one SDK.

Fixes #113708.

(cherry picked from commit d5fcb6c6ab3de96602528dade2d7f75b7e47ddd5)
2026-09-18 11:00:02 -07:00
ethernet
35ea96cd33 fix(desktop): import rmSync in stage-native-deps
prepareDesktopNativeDependencies called a bare `rmSync` that the file never
imported (every sibling here uses `fs.rmSync`), so `npm run build` in
apps/desktop died with "ReferenceError: rmSync is not defined" at
stage-native-deps.mjs:696. That fails EVERY desktop build, which is why the
install e2e's products stage reported "[hermes] app products or command
publication failed" and install.sh exited 1 at HEAD.

Proof: the covering native case in tests-js/desktop-builder.test.mjs is red on
base with exactly that ReferenceError and green with the import.
2026-09-17 16:58:26 -04:00
Austin Pickett
8ffc2f0369 fix(desktop): working WSLg window controls and native Wayland launch (#113247)
* refactor(desktop): extract titleBarOverlayOptions into a tested helper

getTitleBarOverlayOptions in main.ts branched inline on mac/windows/wsl.
Move the decision into titlebar-overlay-width.ts so the WSLg → false rule
(renderer paints its own controls there) is unit-tested next to the width
reservation it pairs with. main.ts keeps a thin caller.

Co-authored-by: null-runner <nicholas.mariani@hotmail.it>

* fix(desktop): renderer-drawn window controls on WSLg

Under WSLg the frameless window (titleBarStyle: 'hidden') had no
minimize/maximize/close. getTitleBarOverlayOptions returned false on the
premise that the RDP host paints replacement controls; it does not for a
frameless window. Electron's native overlay is not the fix either: its
cluster's hit-region drifts from the rendered buttons under the RAIL
compositor.

The renderer now paints Windows-style min/max/close (wslg-window-controls)
routed over a hermes:window-control IPC channel (window-controls.ts →
preload → main). getWindowState reports customWindowControls/isMaximized so
the renderer knows when to mount them and which glyph to show. The cluster
is pinned to TITLEBAR_HEIGHT px (the contrib shell zeroes --titlebar-height
for content subtrees) with 46px native-width caption buttons.

Co-authored-by: Austin Pickett <pickett.austin@gmail.com>

* fix(desktop): wire WSLg window-control IPC and snap maximize onto the work area

Register hermes:window-control in main, report windowControlState from
getWindowState, and re-send window state on maximize/unmaximize so the
renderer's maximize/restore glyph tracks the window.

maximizedBoundsCorrection snaps a WSLg-maximized frameless window back onto
the display work area (RAIL can settle it offset — reported on WSLg 1.0.65).
It is a no-op wherever native maximize already fills the work area, so
healthy compositors are never fought and setBounds cannot loop.

Co-authored-by: null-runner <nicholas.mariani@hotmail.it>

* fix(desktop): call WSLg window-control bridge without the click event

contextBridge structured-clones every argument, and a React SyntheticEvent
is not cloneable, so onClick={controls.minimize} threw "An object could
not be cloned" before ipcRenderer.send ran. The buttons rendered but did
nothing. Wrap the handlers so the bridge is called with no arguments, and
pin that in the test.

* fix(desktop): remove recursive WSLg maximize bounds correction

* fix(desktop): select native Wayland before Electron initialization on WSLg

* fix(desktop): preserve ready pipe and debugger across WSLg launch

* fix(desktop): keep WSLg caption controls scoped to every window

* style(desktop): order window chrome event import

---------

Co-authored-by: null-runner <nicholas.mariani@hotmail.it>
2026-09-17 14:51:18 -04:00
ethernet
b4a294fff9 Merge origin/main; keep PM as plugin dependency owner
Reconcile plugin declarations and validation through PM's atomic generation publication; preserve external runtimes, target markers, and conflict refusal. Keep one source-update completion owner and port upstream lifecycle changes to the PM desktop/runtime paths.
2026-09-17 13:52:05 -04:00
teknium1
2bbff2a405 fix(desktop): exe identity stamping stops retrying when the rcedit binary itself is missing
apps/desktop/scripts/set-exe-identity.mjs::stampExeIdentity retried every rcedit
rejection with 0.5/1/2 s backoff. A failure to SPAWN rcedit (bin/rcedit-x64.exe
missing or not executable) is permanent, so the only effect was a 3.5 s delay
before after-pack.mjs's warning.

The npm rcedit wrapper (@malept/cross-spawn-promise) reports a spawn failure as
CrossSpawnError with the errno on `originalError.code`; a non-zero rcedit exit
("Unable to commit changes") is an ExitCodeError with a numeric `code`. Skip the
retry loop when `originalError.code ?? code` is ENOENT/EACCES and rethrow as-is.

Probe: stub rcedit rejecting with that shape -> before 4 attempts, sleeps
[500,1000,2000]; after 1 attempt, no sleeps. Transient-lock retry unchanged.

Part of #112544 (optional review atom).
2026-09-17 09:19:19 -07:00
liuhao1024
b08ec00a70 fix(desktop): hand preview guest links to the audited opener via a guest preload
Preview-pane guest pages (Streamlit's traceback "Ask Google" / "Ask …"
buttons, plain `<a target="_blank">` anchors) could not open anything: the
`<webview>` has no `allowpopups`, so Chromium drops the popup before any
handler runs. Opening from `setWindowOpenHandler` is banned
(GHSA-9f4c-93c8-jc8g, window-open-policy.ts), so this adds an explicit
click bridge instead:

- main.ts installs a guest preload through `will-attach-webview`, keyed on
  the `persist:hermes-preview` partition only.
- The preload forwards a clicked `_blank` anchor's resolved href to the
  host renderer via `ipcRenderer.sendToHost`; it opens nothing itself.
- PreviewPane admits the URL and routes it through the existing audited
  `hermes:openExternal` IPC.

Salvaged from PR #112959 (squash of its two commits).

Fixes #112941
2026-09-17 09:05:24 -07:00
teknium1
4146fcbd7a test(desktop): drop the source-text cpSync/rmSync guard
Root AGENTS.md bans tests that read source files and regex-match call sites;
the behavioral accented-tree restage test stays and is the invariant.
2026-09-17 00:26:32 -07:00
teknium1
afb9243f9c fix(desktop): widen libuv-only staging to get-windows and the rollback .bak wipe
The salvaged commits drop the cpSync/rmSync imports, but stageGetWindowsInto
landed on main after the PR was opened and still called both, so the module
threw ReferenceError on every platform (7 vitest failures on the cherry-picked
head). Route its six sites through copyFileSync/removeDirSync so get-windows
staging survives the same non-ASCII profile paths as node-pty.

preserveRollbackBackup had the same rmSync no-op as cleanStaleAppOutDir: a
surviving .bak makes renameSync fail, so the previous working build gets wiped
instead of kept as rollback material. Same existsSync -> removeDirSync fallback.

Earlier fixes for the same crash: #60480 (@liuhao1024), #61832 (@danilofalcao),
#76211, #103458, #109273, #111590.
Co-authored-by: liuhao1024 <liuhao1024@users.noreply.github.com>
Co-authored-by: danilofalcao <danilofalcao@users.noreply.github.com>
2026-09-17 00:26:32 -07:00
Kósa
8317fc8f4e fix(desktop): harden before-pack cleanup against the same non-ASCII rmSync no-op
Review follow-ups to the previous commit:

- before-pack.mjs: cleanStaleAppOutDir kept the retrying rmSync (its
  EBUSY resilience is worth keeping) but now verifies the tree is gone
  and falls back to the libuv-backed removeDirSync when the native
  rmSync silently no-ops on a non-ASCII path — previously it logged
  'removed stale unpacked dir' while the stale tree survived.
- removeDirSync: export it for reuse; handle a plain file or symlink at
  the path (rm -rf semantics) instead of throwing ENOTDIR, and tolerate
  broken symlinks via lstat.
- Add a source-level guard test: repo CI runs on Linux where the native
  implementations happen to work, so the accented staging test cannot
  catch a cpSync/rmSync reintroduction there.
- Link the upstream Node issues in the banner (nodejs/node#61878, fixed
  in v24.15.0; nodejs/node#56049, fixed in v24.13.1) and stop
  attributing the native rewrite to Node 24 — only the observation was
  on v24.11.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 00:26:32 -07:00
Kósa
2d88b9b4df fix(desktop): stage node-pty without fs.cpSync/fs.rmSync (non-ASCII Windows paths)
Node 24's native rewrite of fs.cpSync/fs.rmSync mishandles non-ASCII
Windows paths (observed on v24.11.1 with an accented Windows user name,
i.e. the default %LOCALAPPDATA%\hermes install home):

- recursive cpSync fails with EIO "Access is denied" (errno 5) or
  hard-crashes the process (0xC0000409),
- cpSync over an existing file fails with a bogus errno-0
  'unlink' / "The operation completed successfully" error,
- rmSync({recursive, force}) silently deletes nothing, so the
  half-staged dist/node_modules/node-pty tree poisons every retry.

In practice this bricked the desktop build (and thus install/update)
for every Windows user whose profile path contains accented characters:
stage-native-deps.mjs died copying the conpty prebuild, and each rerun
failed earlier on the stale tree the silent rmSync left behind.

Replace all cpSync/rmSync uses with libuv-backed primitives that handle
these paths correctly on the same node.exe: copyFileSync for single
files, a manual mkdirSync+readdirSync+copyFileSync walk for directory
copies, and a manual unlinkSync/rmdirSync walk (verified with existsSync
afterwards, failing loudly instead of silently) for the dest cleanup.

Add a regression test that stages a fake node-pty tree - including a
nested conpty/ payload - into an accented src/dest path twice, so the
restage exercises the delete-then-recopy path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-17 00:26:32 -07:00
teknium1
3dcf0d49ad fix(install): let Rolldown name the missing binding; repair on every OS
The first cut derived the package from `binding-${platform}-${arch}` with an
exact-suffix match, which never matches Windows (`-msvc`) or Linux
(`-gnu`/`-musl`) names, so the repair only ever worked on macOS and
install.sh had to gate it there. Rolldown's own loader already resolves
platform, arch and libc and prints the exact `@rolldown/binding-*` it wanted
in its error chain; parse that instead and drop the gate. Also spawn npm
through a shell on Windows (Node refuses to spawn npm.cmd directly) and trim
the tests to the two invariants (no-op when it loads; installs exactly what
the loader asked for, then re-probes).
2026-09-17 00:25:57 -07:00
Gille
f67cad172e test(desktop): register Rolldown repair tests with Vitest 2026-09-17 00:25:57 -07:00
Gille
ae9f42accf fix(install): repair missing Rolldown bindings 2026-09-17 00:25:57 -07:00
teknium1
f501ebcf1e fix(desktop): size the rcedit retry to the observed lock window and retry any failure
The field report saw the commit still failing 5 s after a first attempt,
so 100/300 ms could exhaust before the scanner released the exe. Use
500 ms / 1 s / 2 s (four attempts, 3.5 s worst case) and retry every
rcedit failure instead of one error-message shape: a held handle can also
surface as a load/open error, and a permanent failure only costs 3.5 s
before after-pack.mjs swallows it.
2026-09-16 17:46:56 -07:00
KoNit-K
8ca920b52a fix(desktop): retry transient rcedit commit failures
Windows desktop rebuilds intermittently hit rcedit's `Fatal error: Unable
to commit changes` while a real-time file scanner holds the freshly packed
Hermes.exe; `stampExeIdentity` called rcedit exactly once, so the stamp
failed on the first lock. Retry the commit with bounded backoff before
surfacing the original error.

Trimmed from #112552: the after-pack.mjs injection seam and its test —
the hook's swallow-and-warn is pre-existing behaviour on main and needs no
change (the reporter's "Could not install the rebuilt desktop app" comes
from hermes_cli/main_desktop.py::_swap_staged_desktop_app's directory
rename, not from the stamp).
2026-09-16 17:46:56 -07:00
brooklyn!
76a4d3b62a feat(desktop): detect the dual-Command gesture on macOS 2026-09-16 19:13:25 -05:00
brooklyn!
691228447d perf(desktop): observe live workspace frame pacing without synthetic sessions 2026-09-16 06:20:40 -05:00
ethernet
0ecfcc2881 fix(ci): predict the desktop smoke's Hermes home from the baked bundle env
Commit desktop bundles can bake environment defaults/clears (--bundle-unset
HERMES_DESKTOP_USER_DATA_DIR, HERMES_HOME=null) that stomp the smoke driver's
--home/--user-data pin, so every native smoke threw "Desktop did not honor the
isolated home and userData directories".

Instead of pinning, the driver replays the bundle env over its launch env
through the same resolver the app runs, seeds the predicted home (bailing
rather than wiping when it is not empty), and verifies the app landed there
via a new hermesHome report on the version bridge. The --user-data equality
check now applies only when the artifact bakes no env.

- Extract the pure path resolver into electron/data-paths.mjs (data-paths.ts
  is now a typed re-export) so Node's type-stripped driver can import it.
- Add applyBundleEnvironment/validateBundleEnvironment as the pure twin of the
  bundle banner, pinned by a lockstep test.
- Record bundleEnv in the install stamp and add readBundledBundleEnv.
- Report hermesHome from hermes:version and assert it equals the predicted home.
2026-09-15 20:32:31 -04:00
ethie
3bbb10c250 fix(desktop): sign Mach-O members inside payload wheel zips before notarization
Apple's notary extracts every zip in the submitted archive and validates
each Mach-O; the notarization failure (err 4000) traced to uv-cache wheel
zips whose native members were never signed — electron-osx-sign reaches
only the extracted archive buckets, not zip members.

signWheelZipMembers (afterPack, darwin) walks the payload's uv-cache
wheel zips, signs .so/.dylib members with the same Developer ID identity
and keychain the chromium signer resolves, repacks, then re-extracts and
runs codesign --verify --strict on every signed member — the same
predicate the notary applies — failing the build loudly if a host's zip
semantics ever drop the embedded signature.

Proven on a real macOS 26 host: codesign may write signatures to extended
attributes for ad-hoc/generic binaries, and plain zip repack silently
drops xattrs; the verify gate converts that host behavior from a silent
notary rejection into a same-run build failure.
2026-09-14 22:22:29 -04:00
ethernet
936fb81005 fix(desktop): cap MSIX copilot extension Id at the 39-char manifest limit
Channel identities splice a 16-hex token into artifactNamePascal
(HermesChannel<token>), so Id="${artifactNamePascal}CopilotKeyProvider"
reached 47 chars and makeappx rejects the manifest with an opaque
maxLength 0x80080204. The Id only discriminates within the package;
DisplayName already carries the variant name, so pin the literal.
2026-09-14 20:30:57 -04:00
ethernet
5b30ae9991 refactor(desktop): delete cross-package retirement migration machinery (round 3 slice 3) 2026-09-14 12:59:17 -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
a3ff6bb986 fix(build): sign nested Chromium with the discovered certificate hash 2026-09-13 20:32:50 -04:00
ethernet
303beaf6fc fix: keep lock files binary and fixture shell paths portable 2026-09-13 18:07:46 -04:00
ethernet
45b5f467d0 fix(desktop): exit native admission probe after ConPTY shell completes 2026-09-13 18:03:57 -04:00
ethernet
f8bdc96033 Merge branch 'ethie/pm-test-audit' into ethie/pm-clean
# Conflicts:
#	tests/scripts/test_bundle_native.py
#	tests/scripts/test_pm_runtime_bundle.py
2026-09-13 18:03:57 -04:00
ethernet
41be32c3c0 test: consolidate desktop contracts around real consumers 2026-09-13 15:10:31 -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
21ddd6a943 fix(desktop): bound complete signing probes without patching fs 2026-09-13 13:05:54 -04:00
ethernet
6e0dd267a2 test(desktop): measure real signing traversal descriptor pressure 2026-09-13 12:52:35 -04:00
ethernet
7d73a180ae Merge branch 'ethie/uv-build-logs' into ethie/pm-clean 2026-09-13 11:18:46 -04:00