# Ops Runbook — v0.25 Search Attribute Registration

> **Required before:** PR-A workers start (register ~3 days ahead of PR-A review opening)
>
> **Owner:** Human operator (this runbook is read-only guidance — do not automate)
>
> **Reference:** `docs/design/session-lifecycle-rebuild-v2.md` §11.2 and `docs/design/session-lifecycle-rebuild-v2-sequencing.md` §7
>
> **Risk of skipping:** `upsertSearchAttributes` calls in PR-A workers will throw `INVALID_ARGUMENT` and break all session phase transitions. Once workflows start writing to these attributes, removing them is unsafe — see Rollback section.

---

## Background

PR-A (`feat(workflow): 7-phase session lifecycle + attachment + lease model`) introduces three new custom Temporal search attributes written by `claudeSessionWorkflow` on every phase transition. They must exist in the Temporal namespace before any PR-A worker starts. Temporal does not auto-create custom search attributes; calling `upsertSearchAttributes` for an unregistered attribute is a non-retryable error.

These attributes are **additive** — they do not replace any existing attributes. The seven existing `ClaudeTempo*` attributes remain unchanged.

| Attribute | Type | Purpose |
|-----------|------|---------|
| `ClaudeTempoAttachedHost` | `Keyword` | Hostname currently holding the attachment lease. Empty string when session is `detached`. Replaces the static `ClaudeTempoHostname` for current-host routing. |
| `ClaudeTempoAttachmentState` | `Keyword` | Current phase of the session state machine: `booting \| attached \| processing \| awaiting \| draining \| detached \| gone`. Written on every phase transition. |
| `ClaudeTempoAttachmentId` | `Keyword` | UUID of the active attachment lease. Empty string when `detached`. Primarily for debugging and attachment eviction. |

---

## Pre-flight Checks

Run these before executing the registration commands.

### 1. Temporal CLI version

```bash
temporal --version
```

Requires **Temporal CLI v1.0.0 or later** (the `temporal` binary, not `tctl`). The commands below use the modern `temporal` CLI. If you have the older `tctl`, see the Appendix.

```bash
# Confirm you have the right binary
temporal env get
# Should show: Address: localhost:7233 (or your cluster address)
```

### 2. Namespace existence

```bash
temporal operator namespace describe --namespace default
```

Expected output includes `Name: default` and `State: Registered`. If your namespace is not `default`, substitute it everywhere below.

### 3. Existing search attributes (verify no collision)

```bash
temporal operator search-attribute list --namespace default
```

Confirm that `ClaudeTempoAttachedHost`, `ClaudeTempoAttachmentState`, and `ClaudeTempoAttachmentId` are **not** in the output. If any already exist, skip that attribute's registration command.

### 4. Temporal server version (for Temporal Cloud users)

Temporal Cloud namespaces support custom search attributes by default. No version requirement beyond having a Cloud namespace. Skip the server version check.

For self-hosted: Temporal Server **v1.20.0 or later** is required for full `Keyword` type support. Check with:

```bash
temporal server version   # if you have access to the server
```

### 5. Permissions

For self-hosted Temporal: the operator running these commands needs `temporal:operator` role or equivalent. For Temporal Cloud: you need a namespace admin API key or be logged in via `temporal login`.

---

## Registration Commands

Run these three commands. **Order does not matter.** Each is idempotent — re-running against an already-registered attribute returns an error that can be safely ignored (see Verification section).

```bash
# 1. Register ClaudeTempoAttachedHost
temporal operator search-attribute create \
  --namespace default \
  --name ClaudeTempoAttachedHost \
  --type Keyword

# 2. Register ClaudeTempoAttachmentState
temporal operator search-attribute create \
  --namespace default \
  --name ClaudeTempoAttachmentState \
  --type Keyword

# 3. Register ClaudeTempoAttachmentId
temporal operator search-attribute create \
  --namespace default \
  --name ClaudeTempoAttachmentId \
  --type Keyword
```

**For Temporal Cloud** — substitute your namespace and add the address and API key:

```bash
temporal operator search-attribute create \
  --address <your-namespace>.tmprl.cloud:7233 \
  --namespace <your-namespace> \
  --api-key $TEMPORAL_API_KEY \
  --name ClaudeTempoAttachedHost \
  --type Keyword

# Repeat for ClaudeTempoAttachmentState and ClaudeTempoAttachmentId
```

**For mTLS auth** — add cert flags:

```bash
temporal operator search-attribute create \
  --address <host>:7233 \
  --namespace default \
  --tls-cert-path $TEMPORAL_TLS_CERT_PATH \
  --tls-key-path $TEMPORAL_TLS_KEY_PATH \
  --name ClaudeTempoAttachedHost \
  --type Keyword

# Repeat for the other two attributes
```

---

## Verification

After registration, confirm all three attributes appear in the namespace:

```bash
temporal operator search-attribute list --namespace default
```

Look for these lines in the output:

```
ClaudeTempoAttachedHost         Keyword
ClaudeTempoAttachmentState      Keyword
ClaudeTempoAttachmentId         Keyword
```

**Optional deeper check** — write a test value and query it back:

```bash
# This requires a running workflow. Once PR-A workers are deployed,
# start a session and then verify the attribute is visible:
temporal workflow list \
  --namespace default \
  --query 'ClaudeTempoAttachmentState = "booting"'
```

A result (even an empty one with no error) confirms the attribute is queryable.

---

## For the `temporal server start-dev` Dev Server

The existing `temporal server start-dev` command in the project docs must be extended with the three new attributes. Update `README.md` and any teammate setup docs to add these flags to the standard dev server invocation:

```bash
temporal server start-dev \
  --search-attribute ClaudeTempoEnsemble=Keyword \
  --search-attribute ClaudeTempoPlayerId=Keyword \
  --search-attribute ClaudeTempoHostname=Keyword \
  --search-attribute ClaudeTempoGitRoot=Keyword \
  --search-attribute ClaudeTempoPlayerType=Keyword \
  --search-attribute ClaudeTempoIsConductor=Bool \
  --search-attribute ClaudeTempoAttachedHost=Keyword \
  --search-attribute ClaudeTempoAttachmentState=Keyword \
  --search-attribute ClaudeTempoAttachmentId=Keyword
```

The three attachment-lifecycle attributes are the last three lines. This command is the
full set as of v0.26.

> **v0.26 retirement note** — `ClaudeTempoStatus` was removed in v0.26 (#174–#178 shim
> removal epic). The `--search-attribute ClaudeTempoStatus=Keyword` line is no longer
> needed; upgrading clusters must manually drop the attribute. See
> [`./v0.26-migration.md`](v0.26-migration.md).

---

## Rollback Instructions

> **Warning:** Once PR-A workers have written values to these attributes (even once), removing the attributes from the namespace is unsafe — any workflow that tries to query by these attributes will return errors. Only deregister if PR-A is fully rolled back **and** no PR-A workers have ever run against this namespace.

### Deregister (only if no PR-A workers have run)

Temporal does not provide a `delete` command for search attributes in the standard CLI. For **self-hosted** deployments, deregistration is done via the Temporal admin DB tooling or by directly patching the namespace record. This is advanced and version-dependent — consult your Temporal server docs.

For **Temporal Cloud**, contact Temporal support to remove search attributes from a namespace; it is not self-service.

**Recommended approach instead of deregistering:** Leave the attributes registered. They are harmless if no code writes to them. Rolling back PR-A workers means the attributes exist but are never written — queries return empty results, which is safe. Re-registering later (for a future v0.25 attempt) is a no-op.

---

## Timing

Per the sequencing memo (§7 and §10.1):

| Event | When |
|-------|------|
| Register attributes in namespace | ~3 days before PR-A opens for review |
| PR-A opens for review | After registration is confirmed |
| PR-A merges to `main` | After CI green + human review |
| `v0.25.0-beta.1` npm publish | After PR-A through PR-D merge |

The 3-day buffer gives time to catch registration failures, propagation delays (Temporal Cloud can take minutes), and any permission issues before the PR-A deadline.

---

## Appendix — Legacy `tctl` Commands

If you are on an older Temporal setup using `tctl` instead of the modern `temporal` CLI:

```bash
tctl --namespace default adm cl add-search-attr \
  --name ClaudeTempoAttachedHost \
  --type Keyword

tctl --namespace default adm cl add-search-attr \
  --name ClaudeTempoAttachmentState \
  --type Keyword

tctl --namespace default adm cl add-search-attr \
  --name ClaudeTempoAttachmentId \
  --type Keyword
```

Verification:

```bash
tctl --namespace default cluster get-search-attr
```

> **Note:** `tctl` is deprecated as of Temporal Server v1.21. Migrate to the `temporal` CLI when possible.

---

## Related Files

- `docs/design/session-lifecycle-rebuild-v2.md` — full design reference
- `docs/design/session-lifecycle-rebuild-v2-sequencing.md` — PR ladder and timing
- `docs/WIRE-PROTOCOL.md` — updated in PR-A to document new search attributes
- `CLAUDE.md` — updated in PR-G with new `temporal server start-dev` command

---

*Authored by tempo-devops | 2026-04-12 | For v0.25.0 launch*
