Repository navigation
feat(host): hold UI adoption on backend changes (BOS_HOLD_ON_BACKEND_CHANGE) + the publish-to-user deploy flow - #897
Open
elliotBraem wants to merge 3 commits into
Open
elliotBraem wants to merge 3 commits into
elliotBraem wants to merge 3 commits into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket
fly-hosting 14 — update prompts: what a publish changes, how it reaches users, proven end to end.
The two hazards, and how this PR addresses each
1. UI/API incoherence during a backend publish
Today the old instance adopts the new UI generation within ~30s of a publish even when the API pin moved and it still runs the old API — the window where new UI calls routes the old API lacks.
Guard: opt-in host mode
BOS_HOLD_ON_BACKEND_CHANGE=1(set later by the hosting adapter's descriptor, ticket 07; default off, so single-box/self-hosted behavior is unchanged). Under the flag,adoptTransactionrefuses to swap whenbootFrozenRestartRemotes(...)is non-empty and recordspendingRestart: [...]alongside the existingrestartRequiredsurface on/healthand/.well-known/version. A generation then moves as a unit: the old instance keeps serving a coherent old UI+API pair until the reconciler (ticket 08) replaces it, and the replacement boots both. Details:lastSeen), so it does not re-derive a refused generation every 30s; the tick outcome reportsheldpendingRestart; it clears on any later adopt that swaps or convergeshost/tests/unit/snapshot-coordinator-hold.test.ts: UI+API publish → held, serving digest unchanged; UI-only publish → swapped; flag off → old adopt-and-flag behavior (restartRequired)2. Old page, new API
After the instance is replaced, open tabs run the previous UI against the new API until Refresh.
Rule (documented, no code): API and plugin changes must stay backward compatible with the previous UI generation for at least one release cycle; a breaking change ships in two deploys. Recorded in
docs/agents/deploy-flow.md(and the plugin-development surface inherits it via the flow link).Banner scope decision (recorded): the banner stays signed-in-only — anonymous navigation picks up new versions naturally; no new complexity. Locked in by the new anonymous-tab spec.
What's here
feat(host)— the coherence guard: env flag read at adopt time,heldoutcome,pendingRestartledger onSnapshotCoordinator, surfaced on/health+/.well-known/version, watch tick mapsheld+ backoff reset,runtime-snapshotomits emptyrestartRequiredtest(regression)—tests/regression/browser/specs/update-banner.spec.ts: UI-only publish → banner within the poll window → Refresh reloads → dismiss lasts the session (reload-proof); backend publish variant (restartRequired/pendingRestartpayload); poll survives a failing endpoint (abort × 2 → recover, no page errors); anonymous tab never pollsdocs(agents)—docs/agents/deploy-flow.md: the flow, hot vs boot-frozen table in plain words, the guard, the backward-compat rule, banner audience + timing (30s watch + 60s poll, ~90s worst case, auto-stop note), what the reconciler restarts, child secrets (NEAR_PRIVATE_KEY,BOS_STORAGE_API_KEY; Railway tokens transitional until ticket 13), framework upgrade flow (bos upgrade --dry-run→upgrade→lock→ merge), rollback pointers (tickets 06/10), failing-deploy table (preflight, composition gate/bos mf check, smoke gate, drift, silent-banner diagnosis). Linked fromAGENTS.md,.github/workflows/README.md, and theeverything-dev-appskill. The fleet console's read-only "behind release X — runbos upgrade" indicator is recorded as a ticket 10 requirement; no button edits a repo.Test evidence
pnpm --filter host test— 46 files, 292 tests green (includes the new hold-decision suite + the watch held-mapping test)pnpm run typecheckgreen;pnpm run lintgreen;pnpm run docs:checkgreenstart:ssrregression stack (Docker, production-like), local run in this branch's worktree. Note on fidelity: the harness boots one instance from a staged config — it cannot publish a real pointer (no FastKV/NEAR credentials) and cannot replace the instance (reconciler, ticket 08), so the spec simulates the newer generation at the/.well-known/versionboundary the banner actually polls. The reconciler-driven variants — a real second instance, Refresh landing on new bytes — are NOT exercised anywhere yet; they need ticket 08's adapter. Tracked as the remaining checkbox in the ticket.tier: releaseregression plane (this spec runs in thestart:ssr+backcompatprojects perplaywright.config.mjs).Doc locations
docs/agents/deploy-flow.mdAGENTS.md(Sync and Publish),.github/workflows/README.md(overview),.agents/skills/everything-dev-app/SKILL.md(Ship step)docs/self-deploy.mdand the workflows README's child Railway-leg detail are current transitional truth owned by tickets 13/07 — the flow doc marks Railway tokens as transitional instead.