Beta #{id} | Electric Cherry {#if loadError}{/if} {#if loading} {:else if !beta && !loadError} {:else if beta}
{#snippet crumbs()}{/snippet} {#snippet actions()}{/snippet}

{closed ? beta.state === 'promoted' ? 'Version promoted' : 'Beta rolled back' : `${passed} / ${servers.length} servers passed`}

{#if test}{coverageSummary(test.plan)}{/if}

{nextAction}

{#if test?.reason}

{test.reason}

{/if}
Started {#if revision.revision > 1}Revision {revision.revision} started {$timeFormat.dateTime(revision.started_at)}{/if} {#if deadline && !closed}Ends · {deadlineText}{/if} {#if beta.closed_at}Finished {$timeFormat.dateTime(beta.closed_at)}{/if}
{#if !closed && test}

Waiting for empty servers uses this window. Pausing or retrying does not extend the deadline. Deploying a revision starts a fresh window.

{/if}
{#if !closed}{/if} {#if closed}{/if} {#if promotion}{/if} {#if rollbackCampaign}{/if}
{#if promotion}

{promotion.pending} servers awaiting activation · {promotion.failed} need attention. Promoting a version does not mean it is already running everywhere.

{/if} {#if flash}{/if} {#if success}

{success}

{/if}
{#if !closed && waiting.length}

Outstanding conditions

{#each waiting as server}

{server.error ?? (server.server_deleted_at ? 'This server was deleted. Recorded progress is retained.' : outstanding(server.server))}

{#if server.error && !server.server_deleted_at}Diagnose{:else if test}View checks{/if}
{/each}
{/if} {#if test}
server.error?.startsWith('deploy:')) ? 'Deployment was incomplete. Roll back before opening a fresh beta.' : undefined} onUpdate={load} />
{/if}
Files and versions ({scripts.length})
{#each scripts as file}
{file.name}{artifactVersionLabel(file.version)}{file.target_rel_path}{file.version?.sha256.slice(0,12)}
{/each}
Deployment progress ({servers.length} servers)
{#each ranked as server}

Files {server.deployed_at ? 'installed' : 'pending'} · Restart {server.restarted_at ? 'submitted' : 'pending'}{#if beta.wait_for_health} · Health {server.healthy_at ? 'verified' : 'pending'}{/if}{#if beta.wait_for_match} · Match {server.match_seen_at ? 'observed' : 'pending'}{/if}

{#if server.provisioning_id}View provisioning{/if}{#if server.error}

{server.error}

{/if}{#if server.stage === 'failed' && !server.server_deleted_at && !test && !closed}{/if}
{/each}
{#if revisions.length}
Revision history ({revisions.length})
{#each revisions as item}
Revision {item.revision} · {item.revision === revision.revision ? 'Current' : item.results?.test?.outcome ?? item.results?.state ?? 'Superseded'} · {$timeFormat.dateTime(item.started_at)} {#if item.notes}

{item.notes}

{/if}
{#each item.files as file}
{file.name} · {artifactVersionLabel(file)}{file.path}{file.sha256.slice(0,12)}
{/each}
{#if item.results?.test} {:else if item.results}
    {#each item.results.servers as server}
  • {server.server} · {server.stage}{server.error ? ` · ${server.error}` : ''}
  • {/each}
{/if}
{/each}
{/if}
{#if activityBefore !== null}{/if}{#if nextBefore !== null}{/if}
{/if} server.server)} timeoutHours={test?.plan.timeoutHours} onUpdate={load} /> {#if acting === 'preview'}

Preparing promotion impact…

{/if} {#if reviewError}{/if} {#if preview}

{preview.servers.filter(server => server.tested).length} tested servers and {preview.servers.filter(server => !server.tested).length} additional eligible servers.

Confirming creates a campaign and queues the file changes. Each server waits until empty and restart capacity is available, then captures rollback snapshots, installs its files, and restarts. Tested servers keep their pre-beta snapshots and only need another activation if their configuration changed or earlier queued work could replace the tested files. Eligibility follows each file's current scope.

Reviewed {$timeFormat.dateTime(preview.createdAt)}. If targets, versions, or composed settings change, you must review again.

{#each preview.blockers as blocker}{/each}
{#each preview.files as file}
{file.name} · {artifactVersionLabel(file)} · {file.servers.length} servers

{file.path} · {file.scope} · SHA {file.sha256.slice(0,12)}

    {#each file.servers as name}
  • {preview.servers.find(server => server.name === name)?.tested ? 'Already tested' : 'Additional target · restart when empty'}
  • {/each}
{/each}
{#if preview.configWrites.length}
Composed configuration files ({preview.configWrites.length}){#each preview.configWrites as write}

{write.server} · {write.path} · {write.sha256?.slice(0,12) ?? write.error}

{/each}
{/if} {:else if acting !== 'preview'}{/if} {#snippet actions()}{#if preview}{/if}{/snippet}