# Ops Runbook — v0.25.0-beta.1 Release Checklist

> **Superseded by v0.26** — this checklist targets the v0.25 release shape, which still
> referenced the legacy `ClaudeTempoStatus` search attribute. For upgrades **to v0.26+**,
> see [`./v0.26-migration.md`](v0.26-migration.md) which includes the operator-side drop
> of `ClaudeTempoStatus` and the breaking-change call-outs.
>
> **Owner:** tempo-devops
> **Status:** Historical — kept for v0.25 → v0.25.x upgrades.
> **Reference:** `docs/design/session-lifecycle-rebuild-v2-sequencing.md` §10
> **Last updated:** 2026-04-12

---

## Overview

v0.25.0-beta.1 is a pre-release tag for the 7-PR rebuild ladder. It publishes to npm under the `beta` dist-tag (not `latest`) and marks the first externally-testable cut of the new attachment lifecycle. End users install via `npm install -g claude-tempo@beta`.

The release workflow already handles pre-release tagging: any tag containing `-` triggers `npm publish --tag beta` and GitHub creates the release with `prerelease: true`. No workflow changes are needed.

---

## 1. Completion Gate — Which PRs Must Be Merged

All seven PRs must be squash-merged to `main` before the tag is pushed.

| PR | Title | Status |
|----|-------|--------|
| PR-A (#115) | 7-phase session lifecycle + attachment + lease model | ✅ Merged `f2ddaf7` |
| PR-B (#121) | Extract claude-code + copilot into `src/adapters/` with registry | ✅ Merged `2dda74eb` |
| PR-C | Full `BaseAttachment` lifecycle + adapter rewire to new wire protocol | ⏳ Not merged |
| PR-D | New adapter verbs + `spawn` outbox entry type + open-set `AgentType` | ⏳ Not merged |
| PR-E | `continueAsNew` boundary lease extension + session migration | ⏳ Not merged |
| PR-F | Lifecycle V2 flag default-on + legacy shim removal | ⏳ Not merged |
| PR-G | Adapter conformance suite + CLAUDE.md / docs cleanup | ⏳ Not merged |

**Minimum for beta.1:** PR-A through PR-D (the new wire surface is stable after PR-D). PRs E–G may land as beta.2 if scheduling requires it. Confirm scope with conductor before bumping.

---

## 2. Blocking Issues

These must be resolved (or explicitly deferred with a decision) before the beta.1 tag:

| Issue | Status | Note |
|-------|--------|------|
| [#116](https://github.com/vinceblank/claude-tempo/issues/116) `blocks-pr-c` | Open | `DetachReason` enum must match design spec before PR-C adapters land |
| [#122](https://github.com/vinceblank/claude-tempo/issues/122) | Open | Spurious copilot-bridge stderr — UX regression, pre-beta fix |

Optional pre-beta improvements (not blocking):
- [#117](https://github.com/vinceblank/claude-tempo/issues/117) — `awaiting` phase unreachable (may defer to post-beta)
- [#118](https://github.com/vinceblank/claude-tempo/issues/118) — `enqueueSpawnUpdate` as-cast (cosmetic)
- [#119](https://github.com/vinceblank/claude-tempo/issues/119) — heartbeat `leaseMs` inconsistency (behavioral, worth fixing)
- [#120](https://github.com/vinceblank/claude-tempo/issues/120) — main loop comment inaccuracy (docs-only)

---

## 3. CHANGELOG Consolidation Template

The current `CHANGELOG.md` has a `## [0.24.1] - Unreleased` stub. On release day, replace it with the full v0.25.0-beta.1 entry. Template below — **fill in PR-C through PR-G entries as they land:**

```markdown
## [0.25.0-beta.1] - YYYY-MM-DD

### Added

- **7-phase session attachment lifecycle** — `claudeSessionWorkflow` now tracks
  `booting | attached | processing | awaiting | draining | detached | gone` phases
  via a lease-based attachment state machine. New updates: `claimAttachment`,
  `processingStart`, `processingEnd`, `forceDetach`, `enqueueSpawn`, `setPreferredHost`,
  `destroy`. New queries: `isDestroyed`, `attachmentInfo`. New signals: `heartbeat`,
  `requestDetach`, `adapterExited` (#115)
- **Three new Temporal search attributes** — `ClaudeTempoAttachedHost` (Keyword),
  `ClaudeTempoAttachmentState` (Keyword), `ClaudeTempoAttachmentId` (Keyword). See
  `docs/ops/v0.25-search-attribute-registration.md` for namespace registration
  instructions (#115)
- **Adapter registry** — `src/adapters/` directory with `BaseAttachment` abstract class,
  `AdapterRegistry` singleton, and two shipped adapters: `InteractiveAttachment`
  (Claude Code CLI) and `CopilotSdkAttachment` (Copilot SDK bridge) (#121)
- **`SessionMetadata.adapterId`** — populated on fresh recruits and sessions; enables
  adapter-specific routing in future PRs (#121)
<!-- PR-C: Add BaseAttachment lifecycle, lease renewal, claimAttachment wiring -->
<!-- PR-D: Add new adapter verbs, spawn entry type, open-set AgentType -->
<!-- PR-E: Add continueAsNew boundary, lease extension -->
<!-- PR-F: LIFECYCLE_V2 flag default-on, shim removal -->
<!-- PR-G: Conformance suite, docs cleanup -->

### Changed

- **`src/channel.ts` → `src/adapters/claude-code/adapter.ts`** — lifted verbatim
  into `InteractiveAttachment` class; `src/channel.ts` deleted (#121)
- **`src/copilot-bridge.ts` → `src/adapters/copilot/adapter.ts`** — lifted verbatim
  into `CopilotSdkAttachment` class; subprocess entrypoint preserved via
  `require.main === module` guard (#121)
- **`docs/WIRE-PROTOCOL.md`** — new §11 attachment lifecycle protocol table (#115)

### Fixed

- **#99** — long tool calls no longer trigger false-stale: `processingStart` /
  `processingEnd` updates suppress stale detection while an adapter is mid-`sendAndWait`;
  15-minute safety timer ejects wedged entries (#115)
- **#102** — graceful stop no longer resurrects sessions: `destroy` update + `isDestroyed`
  query, with runId pinning so a destroyed-then-recreated workflow cannot silently attach
  to the new run (#115)
<!-- PR-C: Fill in fixes from DetachReason reconciliation, leaseMs correction -->

### Breaking Changes (wire protocol)

> **Action required for Temporal Cloud / self-hosted production namespaces:** Register
> the three new search attributes before deploying workers. See
> `docs/ops/v0.25-search-attribute-registration.md`.

The following new Temporal update/signal/query names are stable as of v0.25.0-beta.1.
The compat shim removed in PR-F means workers on v0.24.x cannot co-exist with v0.25
workers in the same ensemble after PR-F lands.
```

---

## 4. Prod-Namespace Search-Attribute Registration

**Must happen before any v0.25 worker is deployed to production.** The three new attributes (`ClaudeTempoAttachedHost`, `ClaudeTempoAttachmentState`, `ClaudeTempoAttachmentId`) and the v0.23 attribute (`ClaudeTempoIsConductor`) are required.

Detailed runbook: `docs/ops/v0.25-search-attribute-registration.md`.

Quick reference for a standard self-hosted or Temporal Cloud namespace:

```bash
# Register all four (idempotent — safe to re-run)
temporal operator search-attribute create --namespace <PROD_NS> --name ClaudeTempoIsConductor   --type Bool
temporal operator search-attribute create --namespace <PROD_NS> --name ClaudeTempoAttachedHost   --type Keyword
temporal operator search-attribute create --namespace <PROD_NS> --name ClaudeTempoAttachmentState --type Keyword
temporal operator search-attribute create --namespace <PROD_NS> --name ClaudeTempoAttachmentId   --type Keyword

# Verify all 10 ClaudeTempo* attributes visible
temporal operator search-attribute list --namespace <PROD_NS> | grep ClaudeTempo
```

Expected output (10 rows — as of v0.25; **v0.26 drops `ClaudeTempoStatus`**, reducing to 9):
```
ClaudeTempoEnsemble         Keyword
ClaudeTempoPlayerId         Keyword
ClaudeTempoHostname         Keyword
ClaudeTempoStatus           Keyword   # removed in v0.26 — see v0.26-migration.md
ClaudeTempoGitRoot          Keyword
ClaudeTempoPlayerType       Keyword
ClaudeTempoIsConductor      Bool
ClaudeTempoAttachedHost     Keyword
ClaudeTempoAttachmentState  Keyword
ClaudeTempoAttachmentId     Keyword
```

---

## 5. Release Pipeline Dry-Run

The `release.yml` workflow is already pre-release–aware. Verify before tagging:

```bash
# Confirm the dist-tag logic handles the pre-release tag format
# (locally — simulates the workflow shell script):
TAG="v0.25.0-beta.1"
if [[ "$TAG" == *-* ]]; then echo "tag=beta"; else echo "tag=latest"; fi
# Expected: tag=beta
```

**GitHub Actions checks:**
- [ ] CI is green on `main` (Node 18, 20, 22 matrix all pass)
- [ ] `NPM_TOKEN` secret is still valid: `npm whoami --registry https://registry.npmjs.org` (run locally with the token to verify)
- [ ] No pending `concurrency: release-${{ github.ref }}` runs that might cancel

---

## 6. Version Bump Steps — Exact Sequence

**NEVER tag before the version bump commit is on `main`.**

```bash
# 1. Ensure main is clean and up to date
git checkout main && git pull origin main

# 2. Bump package.json
#    Edit version: "0.24.0" → "0.25.0-beta.1"
#    (or run: npm version 0.25.0-beta.1 --no-git-tag-version)

# 3. Update CHANGELOG.md
#    Replace "## [0.24.1] - Unreleased" and stub with the full beta.1 entry (§3 above)
#    Ensure "## [Unreleased]" section above it is empty (move any content down)

# 4. Commit the bump
git add package.json CHANGELOG.md
git commit -m "chore: bump version to v0.25.0-beta.1"

# 5. Verify the commit is on main — NEVER tag a non-main commit
git log --oneline -3

# 6. Tag the bump commit
git tag v0.25.0-beta.1

# 7. Push both (main + tag)
git push origin main
git push origin v0.25.0-beta.1
```

GitHub Actions takes over from here: build → test → `npm publish --tag beta` → GitHub Release (prerelease).

---

## 7. Post-Release Verification

```bash
# 1. Install the beta locally
npm install -g claude-tempo@beta

# 2. Verify version
claude-tempo --version
# Expected: 0.25.0-beta.1

# 3. Verify npm dist-tag (beta should NOT be latest)
npm dist-tag ls claude-tempo
# Expected:
#   latest: 0.24.x (or whatever stable was before)
#   beta:   0.25.0-beta.1

# 4. Quick smoke (with Temporal dev server running, all 10 SAs registered):
claude-tempo up  # should start daemon and connect
claude-tempo status
```

**Scheduled features** (cron, recurring messages) run via the local binary. If the binary is outdated, scheduled fires will use old code. Always install locally after release.

---

## 8. Release Day Checklist (Summary)

Copy-paste this into the release PR description or a Notion/GitHub issue on release day:

- [ ] All PRs in the ladder merged to `main` (see §1)
- [ ] Issue #116 resolved or explicitly deferred
- [ ] Issue #122 resolved or explicitly deferred
- [ ] Prod-namespace search attributes registered for all target namespaces (see §4)
- [ ] CI green on `main` (Node 18/20/22)
- [ ] `NPM_TOKEN` secret valid
- [ ] `package.json` version bumped to `0.25.0-beta.1`
- [ ] `CHANGELOG.md` `[0.24.1] - Unreleased` → `[0.25.0-beta.1] - YYYY-MM-DD` (full entry)
- [ ] `## [Unreleased]` section reset to empty
- [ ] Bump commit created: `chore: bump version to v0.25.0-beta.1`
- [ ] Tag created: `v0.25.0-beta.1`
- [ ] Both pushed: `git push origin main && git push origin v0.25.0-beta.1`
- [ ] GitHub Actions release workflow green
- [ ] `npm install -g claude-tempo@beta` + `claude-tempo --version` = `0.25.0-beta.1`
- [ ] `npm dist-tag ls claude-tempo` confirms `latest` unchanged, `beta` = `0.25.0-beta.1`

---

*Authored by tempo-devops | 2026-04-12 | Pre-prep for v0.25.0-beta.1*
