mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 00:03:20 +00:00
487 lines
23 KiB
Markdown
487 lines
23 KiB
Markdown
# Remote Server Update Advisor
|
||
|
||
## Decision
|
||
|
||
Build a remote server update advisor before building any remote self-update
|
||
executor.
|
||
|
||
When the protocol compatibility gate blocks a paired Orca server, Orca should
|
||
tell the user what is wrong, identify the server version and likely install
|
||
shape when it can, and provide copyable platform-specific update guidance. The
|
||
client must not download, install, restart, or run privileged commands on the
|
||
remote machine in this design — not even over an SSH connection Orca already
|
||
holds.
|
||
|
||
The advisor covers **both block directions** reported by
|
||
`evaluateRuntimeCompat` (`src/shared/protocol-compat.ts`):
|
||
|
||
- `server-too-old`: show the remote update guidance described in this doc.
|
||
- `client-too-old`: do not show server guidance. Route to the local update
|
||
flow — the desktop updater (`src/main/updater.ts`) on desktop, the app-store
|
||
update message on mobile.
|
||
|
||
The first implementation adds optional server-side metadata now, but treats it
|
||
as advisory and untrusted:
|
||
|
||
```ts
|
||
type RuntimeUpdateInfo = {
|
||
currentVersion?: string | null
|
||
latestVersion?: string | null
|
||
updateAvailable?: boolean | null
|
||
installKind?: RuntimeInstallKind
|
||
restartKind?: RuntimeRestartKind
|
||
serviceName?: string | null
|
||
installPath?: string | null
|
||
hostArch?: string | null // process.arch, e.g. 'x64' | 'arm64'
|
||
docsUrl?: string
|
||
}
|
||
|
||
type RuntimeInstallKind =
|
||
| 'mac-app'
|
||
| 'mac-homebrew'
|
||
| 'windows-installer'
|
||
| 'linux-appimage'
|
||
| 'linux-deb'
|
||
| 'linux-rpm'
|
||
| 'source'
|
||
| 'unknown'
|
||
|
||
type RuntimeRestartKind = 'desktop' | 'foreground-serve' | 'systemd' | 'unknown'
|
||
```
|
||
|
||
The protocol compatibility gate remains the sole source of truth for whether a
|
||
client can use the server. `latestVersion` and `updateAvailable` improve the
|
||
prompt, but the app must render useful guidance when every field is absent.
|
||
|
||
## Problem
|
||
|
||
Remote Orca servers can be desktop apps, headless Linux AppImages, systemd
|
||
services, Windows machines, deb/rpm package installs, or source builds. A
|
||
client that only says "update the server" leaves users stranded at the exact
|
||
moment the app is blocked. A client that attempts to update the server
|
||
automatically risks running the wrong package manager, requiring `sudo`,
|
||
breaking a service, or mutating a machine the user did not intend to touch.
|
||
|
||
The right first step is to make the blocked state actionable without taking
|
||
control of the remote host.
|
||
|
||
### The cold-start constraint
|
||
|
||
`updateInfo` only exists on servers new enough to ship it — and the servers
|
||
being blocked are, by definition, old. The first generation of blocked servers
|
||
will send **no** `updateInfo` at all. The advisor must therefore be fully
|
||
functional from client-side data alone: the compat verdict, the already-shipped
|
||
`hostPlatform` field on `RuntimeStatus` (`src/shared/runtime-types.ts` — itself
|
||
optional; servers older than it land in the unknown-install guide), the
|
||
paired endpoint's port, and client-side release metadata. `updateInfo` is an
|
||
investment that pays off the *next* time a server falls behind. This also
|
||
means steps 1–2 of the rollout should reach a release as early as possible,
|
||
independent of the UI.
|
||
|
||
## Goals
|
||
|
||
- Show a clear "Update server required" prompt when protocol compatibility
|
||
blocks a paired server, reusing the existing entry points
|
||
(`HostSectionHeaderMenu.tsx`, `RuntimeEnvironmentsPane.tsx`).
|
||
- Handle `client-too-old` by routing to the local update flow instead.
|
||
- Show running and required protocol versions from the compat verdict.
|
||
- Add optional update metadata to runtime status so future blocked states can
|
||
display server version, latest known version, install kind, and restart kind.
|
||
- Provide copyable update commands or manual steps for the detected
|
||
platform/arch/install kind, and a useful fallback when unknown.
|
||
- Support the SSH use case: the guidance is written to be pasted into the
|
||
user's own SSH session on the server. Orca does not run it.
|
||
- Never block the compat gate or the status path on a network call.
|
||
|
||
## Non-Goals
|
||
|
||
- Do not run update commands on the remote server, via any transport —
|
||
including SSH connections Orca already holds for SSH workspaces.
|
||
- Do not invoke `sudo`, package managers, installers, or service restarts.
|
||
- Do not silently replace AppImages or desktop apps.
|
||
- Do not require exact app-version equality between client and server
|
||
(protocol versions decide compatibility; app versions are display/advice).
|
||
- Do not build a new stateful update service for the MVP.
|
||
- Do not cover the SSH relay (`src/main/ssh/ssh-relay-deploy.ts`). The relay
|
||
is client-deployed into content-hash-versioned install dirs and self-heals on
|
||
version mismatch; it never needs this advisor. This doc is about paired
|
||
`orca serve` runtime servers.
|
||
|
||
## Design
|
||
|
||
### Trust model
|
||
|
||
Every `updateInfo` field crosses a trust boundary: it is produced by a remote,
|
||
possibly compromised, server and some of it is rendered into commands the user
|
||
is invited to paste into a root shell. Rules:
|
||
|
||
- Command blocks are rendered **only from client-owned templates**. Server
|
||
data may select a template or fill a validated placeholder; it is never
|
||
concatenated into a command as free text.
|
||
- `serviceName`: must match `^orca[A-Za-z0-9@:._-]{0,75}\.service$` — the
|
||
mandatory `orca` prefix mirrors the trust rule cgroup-based systemd
|
||
detection already applies ("trusted only when the unit name starts with
|
||
`orca`"), so a compromised server cannot aim the rendered
|
||
`sudo systemctl restart` at an unrelated unit such as `sshd.service`, and
|
||
the leading alphanumeric means the value can never parse as a `systemctl`
|
||
flag. Otherwise it is discarded and the default `orca-serve.service` is
|
||
used.
|
||
- `installPath`: must be absolute, end in `.AppImage`, and match
|
||
`^[A-Za-z0-9/._~+-]{1,256}$` (no spaces, quotes, or shell metacharacters) or
|
||
it is discarded and the documented default `/opt/orca/orca-linux.AppImage`
|
||
is used. The `.AppImage` suffix is load-bearing: the template `sudo mv`s
|
||
onto this path, so without it a malicious server could aim the overwrite at
|
||
an arbitrary root-owned file. Only the Linux AppImage template renders
|
||
`<install-path>`; typical Windows paths (drive letters, backslashes) fail
|
||
the regex anyway, and the Windows and macOS guides never embed a path in a
|
||
command.
|
||
- `currentVersion` / `latestVersion`: must match
|
||
`^\d+\.\d+\.\d+(-[A-Za-z0-9.-]{1,40})?$` or they are not displayed.
|
||
- `docsUrl`: parsed with `new URL()` and rendered only if the origin is
|
||
`https://github.com` and the **parsed** pathname is `/stablyai/orca` or
|
||
starts with `/stablyai/orca/`; otherwise the client's built-in links are
|
||
used. Checking the parsed pathname defeats `…/orca/../other-repo`, which a
|
||
raw string-prefix check passes and browsers normalize to another repo; the
|
||
trailing slash rules out sibling names like `stablyai/orca-foo`.
|
||
- Port: taken from the client's own paired endpoint (an integer the client
|
||
already validated), never from server metadata. Behind a tunnel or proxy it
|
||
can differ from the server's listen port, so guides treat it as a hint.
|
||
- Enum fields (`installKind`, `restartKind`): unrecognized values are treated
|
||
as `unknown`. `hostArch` is a free-form `process.arch` string: anything
|
||
other than a recognized arch (`x64`, `arm64`) is treated as absent.
|
||
|
||
Values that fail validation are treated as absent — no error, no partial
|
||
rendering.
|
||
|
||
### Runtime status metadata
|
||
|
||
Add optional `updateInfo` to `RuntimeStatus`, alongside the existing
|
||
`runtimeProtocolVersion` / `capabilities` / `hostPlatform` fields, emitted from
|
||
the same place status is stamped today (`src/main/runtime/orca-runtime.ts`).
|
||
Older servers omit it; all client handling must be defensive.
|
||
|
||
Detection is computed **once at server startup and cached** — never on the
|
||
status hot path, which is polled. No package-manager subprocess may run per
|
||
status request. Signals, in order of reliability:
|
||
|
||
- `source`: `app.isPackaged` is false.
|
||
- `linux-appimage`: `process.env.APPIMAGE` is set (the AppImage runtime sets
|
||
it); its value is `installPath`.
|
||
- `windows-installer`: win32 and `process.execPath` under the NSIS install
|
||
location.
|
||
- `mac-app`: darwin and `process.execPath` inside a `.app` bundle. Homebrew
|
||
cask installs copy the app into `/Applications`, so `mac-homebrew` is not
|
||
reliably distinguishable at runtime; when ambiguous, report `mac-app` (the
|
||
mac guide includes a Homebrew alternative). `mac-homebrew` stays in the enum
|
||
for explicit future configuration.
|
||
- `linux-deb` / `linux-rpm`: non-AppImage packaged Linux install (execPath
|
||
under the package install dir); the deb-vs-rpm distinction may use a single
|
||
package-ownership check at startup. If the check is skipped or fails, report
|
||
`unknown`.
|
||
- `restartKind: systemd`: `/proc/self/cgroup` names a `.service` unit under
|
||
`/system.slice/` — which also yields `serviceName`. A user-slice `.service`
|
||
cgroup is trusted only when the unit name starts with `orca`: children share
|
||
their parent's cgroup, so a manual `orca serve` inside a terminal or
|
||
multiplexer that itself runs as a user service (e.g.
|
||
`gnome-terminal-server.service`, a tmux user unit) would otherwise
|
||
false-positive. `INVOCATION_ID` alone is not trusted for the same
|
||
inheritance reason.
|
||
- `restartKind: desktop` when serving from the full desktop app;
|
||
`foreground-serve` for `orca serve` with no systemd markers.
|
||
|
||
Detection must prefer `unknown` over guessing, and prefer the broader kind
|
||
over an unverified narrower one.
|
||
|
||
`hostArch` is `process.arch`, needed because Linux assets differ by arch
|
||
(`orca-linux.AppImage` vs `orca-linux-arm64.AppImage`). When absent (old
|
||
server), the guide shows the x64 command with an arm64 note rather than
|
||
guessing.
|
||
|
||
### Client prompt surfaces
|
||
|
||
Use the same advisor content everywhere a blocked remote server surfaces:
|
||
|
||
- Runtime environment settings row (`RuntimeEnvironmentsPane.tsx`): the
|
||
strongest detail view — versions, detected install/restart kind, copyable
|
||
commands, docs links, and "Check again" (re-fetches status and re-evaluates
|
||
compatibility). "Check again" disables with a spinner while the recheck is
|
||
in flight; on success the blocked state clears back to the normal server
|
||
view, and on continued incompatibility an inline note confirms the server
|
||
is still out of date — never a click with no visible response.
|
||
- Sidebar host menu (`HostSectionHeaderMenu.tsx`): keep the existing
|
||
"Update server required" action; deep-link to the settings row.
|
||
- Blocked action dialog or toast: the compat gate already throws a
|
||
`RUNTIME_COMPAT_BLOCK_CODE`-tagged error
|
||
(`src/renderer/src/runtime/runtime-protocol-compat.ts`), so surfaces can
|
||
distinguish a version block from a transport failure and offer the same
|
||
deep-link.
|
||
|
||
The advisor needs the blocked server's `RuntimeStatus` (`hostPlatform`,
|
||
`updateInfo`) and the compat verdict even though the gate throws. Today the
|
||
`RUNTIME_COMPAT_BLOCK_CODE` error carries only a message and code, discarding
|
||
both: attach the verdict and `RuntimeStatus` to that error (e.g.
|
||
`error.verdict`, `error.status`), and have "Check again" read status through
|
||
the raw non-asserting RPC (`status.get` + `unwrapRuntimeRpcResult`) rather
|
||
than `getRuntimeEnvironmentStatus`, so detection and versions can render
|
||
while the environment stays blocked.
|
||
|
||
Primary copy for `server-too-old`:
|
||
|
||
> This Orca server needs an update before this client can use it.
|
||
|
||
For `client-too-old`, keep the existing `describeRuntimeCompatBlock` message
|
||
and offer the local updater action instead of any server guidance.
|
||
|
||
When detection is available, include a line such as:
|
||
|
||
> Detected: Linux AppImage, systemd service.
|
||
|
||
The line comma-joins only the fields that resolved — e.g. "Detected: Linux
|
||
AppImage" when `restartKind` is `unknown` — and is omitted entirely when both
|
||
are unknown.
|
||
|
||
Command blocks are copyable one block at a time. A command block is a
|
||
suggestion for the user to run on the server (typically over their own SSH
|
||
session), never a command Orca executes.
|
||
|
||
The mobile client duplicates the compat evaluators (see the header comment in
|
||
`protocol-compat.ts`); the guide matrix and copy should live in shared,
|
||
dependency-free modules so mobile can mirror them.
|
||
|
||
### Update guide matrix
|
||
|
||
The client owns a guide matrix keyed by `hostPlatform`, `hostArch`,
|
||
`installKind`, and `restartKind`. It ships with the client and needs no server
|
||
deploy to change. Placeholders below (`<install-path>`, `<service-name>`,
|
||
`<port>`) are filled from validated metadata or documented defaults
|
||
(`/opt/orca/orca-linux.AppImage`, `orca-serve.service`, `6768` — matching
|
||
`docs/reference/headless-linux-server.md`).
|
||
|
||
All Linux download commands write to a sibling temp file and `mv` into place.
|
||
Overwriting a running binary in place fails with `ETXTBSY`; `mv` within the
|
||
same directory is an atomic rename that works while the old version runs. Do
|
||
not stage in `/tmp` — a cross-filesystem `mv` degrades to a non-atomic copy
|
||
into the destination path, which reintroduces the same failure.
|
||
|
||
Linux AppImage (x64; use `orca-linux-arm64.AppImage` on arm64), download and
|
||
swap:
|
||
|
||
```bash
|
||
sudo curl -fL https://github.com/stablyai/orca/releases/latest/download/orca-linux.AppImage \
|
||
-o <install-path>.new
|
||
sudo chmod +x <install-path>.new
|
||
sudo mv <install-path>.new <install-path>
|
||
```
|
||
|
||
Then restart:
|
||
|
||
- systemd service: `sudo systemctl restart <service-name>`
|
||
- foreground server: stop the running `orca serve` (Ctrl+C) and re-run your
|
||
usual serve command, e.g.
|
||
`LIBGL_ALWAYS_SOFTWARE=1 <install-path> serve --port <port>` plus any flags
|
||
you normally pass (such as `--pairing-address`). The advisor cannot know the
|
||
original command line, so it must not present an invented one as complete.
|
||
|
||
Debian or Ubuntu package (assets are versioned: `orca-ide_<version>_amd64.deb`,
|
||
or `_arm64.deb` on arm64; when release metadata supplies the exact asset URL
|
||
the advisor prepends a matching `curl -fLO <asset-url>` line — here and for
|
||
rpm — otherwise it links the releases page and the command uses the downloaded
|
||
filename):
|
||
|
||
```bash
|
||
curl -fLO <asset-url> # included when release metadata supplies the exact asset URL
|
||
sudo apt install ./orca-ide_<version>_amd64.deb
|
||
sudo systemctl restart <service-name> # if running as a service
|
||
```
|
||
|
||
Fedora, RHEL, or compatible RPM package (`orca-ide-<version>.x86_64.rpm` /
|
||
`.aarch64.rpm`):
|
||
|
||
```bash
|
||
curl -fLO <asset-url> # included when release metadata supplies the exact asset URL
|
||
sudo dnf install ./orca-ide-<version>.x86_64.rpm
|
||
sudo systemctl restart <service-name> # if running as a service
|
||
```
|
||
|
||
macOS desktop app:
|
||
|
||
- Open Orca on that Mac and use the in-app update flow, or download the latest
|
||
`orca-macos-<arch>.dmg` from the releases page and replace the app.
|
||
- If it was installed with Homebrew: `brew update && brew upgrade --cask
|
||
stablyai/orca/orca --greedy` (the cask is marked `auto_updates`, so plain
|
||
`brew upgrade` skips it; `--greedy` is required).
|
||
- Restart the paired server after the app updates.
|
||
|
||
Windows installer:
|
||
|
||
- Open Orca on that Windows machine and use the in-app update flow, or
|
||
download and run
|
||
`https://github.com/stablyai/orca/releases/latest/download/orca-windows-setup.exe`.
|
||
- Restart Orca (or the `orca serve` process) after installation.
|
||
|
||
Source build: pull and rebuild; link the repo README.
|
||
|
||
Unknown install:
|
||
|
||
- Show the server platform and version if known.
|
||
- Link `docs/reference/headless-linux-server.md` (on GitHub) and the releases
|
||
page.
|
||
- Tell the user to update Orca on the server machine, restart the server, and
|
||
click "Check again".
|
||
|
||
Exact deb/rpm filenames are versioned and must come from release metadata. If
|
||
the client does not know the exact asset URL, it shows manual download
|
||
guidance — it never invents a versioned filename. The unversioned
|
||
`releases/latest/download/` URLs are stable only for the artifacts with
|
||
unversioned names in `config/electron-builder.config.cjs`:
|
||
`orca-linux.AppImage`, `orca-linux-arm64.AppImage`, `orca-macos-<arch>.dmg`,
|
||
and `orca-windows-setup.exe`.
|
||
|
||
### Release metadata
|
||
|
||
No new update service. The desktop updater already publishes electron-updater
|
||
manifests at a stable generic feed
|
||
(`https://github.com/stablyai/orca/releases/latest/download` — see
|
||
`src/main/updater.ts`): `latest-linux.yml`, `latest-mac.yml`, and `latest.yml`
|
||
carry the latest version and asset filenames (the arm64 Linux release
|
||
publishes its own `latest-linux-arm64.yml` variant). The advisor picks the
|
||
manifest matching the *server's* platform/arch — not the client's — and reuses
|
||
it for `latestVersion` / `updateAvailable` / exact asset names.
|
||
|
||
Constraints, all inherited from known updater behavior:
|
||
|
||
- The fetch is best-effort with a short timeout, done lazily when the advisor
|
||
is shown — never on the compat gate or status path. Failure means
|
||
`updateAvailable: null` and version-less guidance, not an error.
|
||
- During release transitions, GitHub can expose a release before its assets
|
||
are reachable (see the walk-back logic in `updater.ts`). A fetched manifest
|
||
that names unreachable assets must degrade to the manual releases-page link,
|
||
not a broken copyable URL.
|
||
- Results may be cached briefly on the client; staleness only affects advice
|
||
text, never compatibility.
|
||
- If a server ever populates `latestVersion` / `updateAvailable` itself (e.g.
|
||
from its own updater check at startup), the client-fetched manifest wins:
|
||
server values are startup-stale hints and are never refreshed on the status
|
||
path.
|
||
|
||
A stateful update service is only needed later for staged rollouts, kill
|
||
switches, signed org policy, private channels, or fleet controls.
|
||
|
||
## Rollout
|
||
|
||
Ordered so each piece is independently safe; one PR works, but steps 1–2 may
|
||
land earlier on their own (see the cold-start constraint):
|
||
|
||
1. `RuntimeUpdateInfo` type in `src/shared/runtime-types.ts`; startup-cached
|
||
detection in main; emit `updateInfo` in runtime status
|
||
(`src/main/runtime/orca-runtime.ts`).
|
||
2. Validation module for server-supplied fields (shared, dependency-free, unit
|
||
tested against injection inputs).
|
||
3. Client-side guide matrix keyed by platform/arch/installKind/restartKind
|
||
with the fallback chain above (shared module for mobile reuse).
|
||
4. Advisor UI in `RuntimeEnvironmentsPane.tsx` with both verdict directions:
|
||
`server-too-old` → guidance; `client-too-old` → local updater action.
|
||
"Check again" re-fetches status.
|
||
5. Deep-links from `HostSectionHeaderMenu.tsx` ("Update server required") and
|
||
from `RUNTIME_COMPAT_BLOCK_CODE` error surfaces.
|
||
6. Lazy release-metadata lookup (`latest-*.yml`) for `latestVersion`,
|
||
`updateAvailable`, and exact deb/rpm asset names.
|
||
|
||
## Testing
|
||
|
||
- Unit: detection mapping per platform fixture (APPIMAGE env, systemd cgroup
|
||
line — including a user-slice terminal-service cgroup that must not map to
|
||
`systemd`, `app.isPackaged`); every validation regex rejects
|
||
shell-metacharacter payloads (`; curl … | sh`, backticks, `$()`, newlines) and accepts the
|
||
documented defaults.
|
||
- Unit: guide matrix snapshot per (platform, arch, installKind, restartKind)
|
||
key plus the all-fields-absent cold-start case — the doc's own commands are
|
||
the expected output.
|
||
- Unit: verdict branching — `client-too-old` never renders server commands.
|
||
- Extend `runtime-compatibility-test-fixture.ts` (today it only builds
|
||
compatible-status responses) with blocked-status fixtures with and without
|
||
`updateInfo`.
|
||
- Manifest fetch: timeout and 404 paths yield `updateAvailable: null` and the
|
||
fallback guide; no path throws into the compat gate.
|
||
|
||
## Alternatives Considered
|
||
|
||
### Full remote self-update
|
||
|
||
The client could download an asset on the remote machine, install it, and
|
||
restart the server — including over an SSH connection Orca already holds for
|
||
SSH workspaces.
|
||
|
||
Rejected for the first version. Remote hosts vary too much: AppImages, systemd
|
||
services, desktop apps, Windows installers, package managers, and source
|
||
builds all have different permission and restart models. Running install
|
||
commands remotely — even over Orca-owned SSH — creates a trust and audit
|
||
problem, especially when `sudo` or service restarts are involved. It can be
|
||
revisited as an explicit, capability-gated flow that asks for confirmation and
|
||
shows exactly what will run.
|
||
|
||
### Protocol-only error message
|
||
|
||
Keep the current compatibility block and only say the server is too old.
|
||
|
||
Rejected. Technically safe but not user-safe: the user still has to infer
|
||
which machine is stale, how Orca was installed there, which asset to download,
|
||
and whether a service restart is needed.
|
||
|
||
### Client-only latest-version check
|
||
|
||
Compare the remote protocol against the client's own app version or update
|
||
feed, with no server metadata.
|
||
|
||
Rejected as the only mechanism. The client's platform and install channel need
|
||
not match the server's — a macOS client may pair with a Linux AppImage server.
|
||
Client-side release data feeds the guides, but cannot describe the server's
|
||
install shape.
|
||
|
||
### Require a new Orca update server
|
||
|
||
Rejected for the MVP. The advisor needs best-effort latest-version and asset
|
||
metadata, which the existing electron-updater manifests already provide.
|
||
|
||
### Exact version lockstep
|
||
|
||
Require the server to run the exact client app version.
|
||
|
||
Rejected. The protocol contract in `protocol-compat.ts` already defines
|
||
compatibility, and exact equality would fail compatible pairs. App versions
|
||
are for display and advice only.
|
||
|
||
## Deferred / Open Questions
|
||
|
||
### From 2026-07-06 review
|
||
|
||
- **Blocked surface left undecided between dialog and toast** — Client prompt surfaces (P1, design-lens, confidence 75)
|
||
|
||
Dialog and toast are not interchangeable: a dialog is modal, traps focus,
|
||
and requires explicit dismissal, while a toast is transient, non-blocking,
|
||
and typically announced as a polite live region. The doc says "Blocked
|
||
action dialog or toast" without choosing, leaving the implementer to pick
|
||
focus-management and dismissal behavior on their own, risking inconsistent
|
||
UX if this surface and future blocked-action surfaces land differently.
|
||
|
||
- **Advisor assumes the blocked user can run sudo on the server** — Goals / Client prompt surfaces (P2, product-lens, adversarial, confidence 75)
|
||
|
||
Every actionable path the advisor produces is a sudo command pasted into
|
||
the user's own SSH session, which assumes the blocked user is also the
|
||
server's administrator. On shared or team-administered servers the
|
||
copyable commands are un-runnable and the block stays exactly as
|
||
unactionable as the plain "update the server" message. A copyable "send
|
||
these steps to whoever administers this server" handoff would keep the
|
||
goal met for non-admin users.
|
||
|
||
- **Server-metadata pipeline is sequenced first but delivers no value to currently-blocked servers** — Rollout / cold-start constraint (P2, product-lens, adversarial, confidence 75)
|
||
|
||
By the doc's own logic, rollout steps 1–2 (updateInfo schema, cached
|
||
detection, trust model) help no server blocked today — they pay off only
|
||
the next time a server falls behind, and that window's population is
|
||
unvalidated. The client-only advisor (steps 3–5) already delivers the full
|
||
user-facing unblock; consider shipping it as v1 and treating steps 1–2 and
|
||
6 as a fast-follow once advisor engagement is confirmed, or naming the
|
||
expected frequency of the payoff window to justify front-loading a
|
||
permanent wire contract.
|