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

{presentation.label}

{test && test.servers.length ? `${passed}/${servers.length} test servers passed` : test && !closed ? 'Awaiting test results' : 'Test results not recorded'}

{#if beta.state === 'promoted' && promotion} {:else if beta.state === 'rolled-back' && rollbackCampaign} {:else if closed} {:else if presentation.key === 'ready'} {:else if presentation.key === 'attention'} {:else if presentation.key === 'paused' && !testExpired}{/if} {#if !closed || promotion || rollbackCampaign}
More actions
{#if !closed} {#if test && !hasDeletedServers} {#if ['active','passed'].includes(test.outcome) && !testExpired}{/if} {#if ['paused','failed'].includes(test.outcome) && !testExpired}{/if} {/if} {:else}{/if}
{/if}
{#if blocker}

{blocker}

{/if} {#if retryBlocked && !closed && ['paused','failed'].includes(test?.outcome ?? '')}

Deployment was incomplete. Roll back before opening a fresh beta.

{/if} {#if promotion && (promotion.pending || promotion.failed)}

{#if promotion.pending}{promotion.pending} awaiting activation{/if}{#if promotion.pending && promotion.failed} · {/if}{#if promotion.failed}{promotion.failed} need attention{/if}

{/if} {#if deadline && !closed && test?.outcome !== 'passed'}
{deadlineText}

Deadline: . Waiting, pausing, and retrying use the same testing window. Deploying a new revision starts a fresh window.

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

{success}

{/if}
{#if test} {#key id}{/key} {:else}

Test servers

{#each ranked as server (server.server)} {@const expanded = legacyExpanded[server.server] ?? server.stage === 'failed'} {:else}{/each}
ServerDeploymentTest resultDetails
Not recorded
No servers recorded.
{/if}
Files and versions ({scripts.length})
{#each scripts as file}
{file.name} · {artifactVersionLabel(file.version)}

{file.target_rel_path}

{#if file.version}

SHA {file.version.sha256}

{/if}
{/each}
Beta details
Revision {revision.revision}Started {$timeFormat.dateTime(beta.opened_at)}{#if revision.revision > 1}Revision started {$timeFormat.dateTime(revision.started_at)}{/if}{#if beta.closed_at}Finished {$timeFormat.dateTime(beta.closed_at)}{/if}
{#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} {#snippet footer()}
{#if activityBefore !== null}{/if}{#if nextBefore !== null}{/if}
{/snippet}
{/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}