cf-switch-backend · git:20261011.82fab54 · 2026-10-11 · sha256 351dd79f9d199d11

cf-switch-backend git:20261011.82fab54A

Immutable. This exact content never changes and is served at /api/v1/blob/351dd79f9d199d11.

---
name: cf-switch-backend
description: "Switch tracking and delivery between Google Sheets, Airtable and local files, migrating records. \"move tracking to Airtable\""
disable-model-invocation: false
argument-hint: "[local | airtable | google_sheets] [--status]"
effort: high
---

# Switch Tracking Backend

Switch ContentForge's tracking and delivery backend between **Google Sheets + Drive**, **Airtable**, or **Local filesystem**. Optionally migrate existing tracking data and output files to the new backend.

## Execution gate

Before any record or file is copied, or the brand profile changes, run the dry run (what would move, from where to where, how many records and files) and ask the user to type `yes` (Step 4). Anything else cancels with no files changed. Migration is additive and never deletes source data.

## When to Use

- Switching from local (default) to a cloud backend for collaboration
- Migrating from Google to Airtable (or vice versa) for simpler auth
- Downgrading to local when cloud access isn't needed
- Checking current backend status before switching

**Running in Cowork?** The "local" backend writes to the ephemeral sandbox filesystem — files vanish at session end. For Cowork team setups, use the Google (Drive) route and run `/contentforge:cf-cowork-setup` first to wire the Drive folder layout.

## How to Use

```
/contentforge:cf-switch-backend airtable
/contentforge:cf-switch-backend google_sheets
/contentforge:cf-switch-backend local
/contentforge:cf-switch-backend --status
```

## What This Command Does

### Step 1: Identify Current Backend

Read the active brand profile's `tracking.backend` field.

Report current state:
```
Current backend: local
Records: 47 tracking records (~/.claude-marketing/{brand}/tracking/tracking.json)
Output files: 42 files in ~/.claude-marketing/{brand}/outputs/{YYYY}/{MM}/
```

### Step 2: Validate Target Backend

**If switching to Airtable:**
1. Check `AIRTABLE_TOKEN` environment variable exists
2. If missing, guide through setup:
   - Go to [airtable.com/create/tokens](https://airtable.com/create/tokens)
   - Create a Personal Access Token with `data.records:read` and `data.records:write` scopes
   - Select the base that will hold tracking data
   - Set the environment variable: `export AIRTABLE_TOKEN=patXXXXXXXX`
3. Ask for the Airtable Base ID (from the base URL: `airtable.com/appXXXXXXXXX/...`)
4. Verify access by running:
   ```
   python {scripts_dir}/airtable-tracker.py --action init --base-id {base_id}
   ```

**If switching to Google Sheets + Drive:**
1. Check Google credentials at `~/.claude-marketing/google-credentials.json`
2. If missing, guide through setup:
   - Go to Google Cloud Console > IAM & Admin > Service Accounts
   - Create a project (or use existing)
   - Enable Google Sheets API and Google Drive API
   - Create a Service Account and download the JSON key
   - Save to `~/.claude-marketing/google-credentials.json`
   - Create a Google Sheet and share it with the service account email (Editor)
   - Create a Google Drive folder and share it with the service account email (Editor)
3. Ask for the Google Sheet ID and Drive folder ID
4. Verify access by running:
   ```
   python {scripts_dir}/sheets-tracker.py --action init --sheet-id {sheet_id}
   ```

**If switching to Local:**
- No setup needed — works immediately
- Run init to ensure directory exists:
  ```
  python {scripts_dir}/local-tracker.py --action init --brand "{brand}"
  ```

### Step 3: Offer Migration

If the current backend has existing records:

```
You have 47 tracking records and 42 output files on 'local'.
Would you like to migrate this data to Airtable?
  1. Yes — Copy all records and files to the new backend
  2. No — Start fresh (existing data preserved but not synced)
  3. Skip — Just switch, decide on migration later
```

**If yes**, run:
```
python {scripts_dir}/backend-migrator.py --action migrate --brand "{brand}" --from {current} --to {target} [backend-specific args]
```

`{current}` and `{target}` must be one of `local`, `airtable`, `google_sheets` — the migrator rejects any other value.

Report migration results:
```
Migration complete:
  Records migrated: 47
  Files migrated: 42
  Files failed: 0
  Source data preserved at: ~/.claude-marketing/{brand}/tracking/tracking.json
                        and ~/.claude-marketing/{brand}/outputs/{YYYY}/{MM}/
```

### Step 4: Get explicit approval, then update the brand profile

**Do not write the brand profile until the user explicitly approves this exact change** — even when Step 3 had nothing to migrate and asked nothing. Show the change first:

```
About to switch {brand}'s tracking backend:
  tracking.backend: {current} → {target}
  New config: {backend-specific keys and values, secrets masked}
  Existing data: preserved in place (never deleted)
Type "yes" to apply, anything else to cancel.
```

Anything other than an explicit yes cancels the switch with no files changed. This gate is the safety boundary on every platform: Claude Code honours this skill's `disable-model-invocation: true`, but other hosts (Codex documents the field as unsupported) can invoke the skill without the user typing its command.

On approval, update the brand profile JSON:
- Set `tracking.backend` to the new backend value
- Fill in backend-specific config (base_id, sheet_id, folder_id, etc.)

### Step 5: Report

```
Backend switched to Airtable.
  New tracking records → Airtable base appXXXXXX
  Output files → Airtable attachment fields
  Previous data preserved at: ~/.claude-marketing/{brand}/tracking/tracking.json
                          and ~/.claude-marketing/{brand}/outputs/{YYYY}/{MM}/

Run /contentforge:cf-switch-backend --status to verify anytime.
```

## Backend Comparison

| Factor | Google Sheets + Drive | Airtable | Local |
|--------|----------------------|----------|-------|
| Auth setup | Service account (~5 min) | Personal token (~2 min) | None |
| Tracking | Google Sheets | Airtable records | JSON file |
| File delivery | Google Drive | Attachment field | Local filesystem |
| Collaboration | Share via Google | Share via Airtable | Single user |
| Free tier | 15GB Drive | 1,000 records + 10GB | Unlimited |
| Offline | No | No | Yes |

## Check Status

Run with `--status` flag to check current backend health:

```
/contentforge:cf-switch-backend --status
```

This runs:
```
python {scripts_dir}/backend-migrator.py --action status --brand "{brand}" --from {current_backend}
```

## Important Notes

- **Migration is additive** — source data is never deleted
- **Migration is idempotent for records that carry a `requirement_id`** — dedup is keyed on that field, so records without one are re-written on every run
- **Migration is resumable on the same basis** — re-running after an interruption skips whatever already landed with a matching `requirement_id`
- **Migrating *from* `google_sheets` moves records only** — downloading the linked Drive files is not implemented yet, so rows arrive without their attachments (the source Drive copies are untouched)
- **You can switch back anytime** — data is preserved on all backends you've used
- **The pipeline uses whichever backend is set** in `tracking.backend` — switching takes effect immediately for the next content run

## Related Skills

- **[/contentforge:cf-style-guide](../cf-style-guide/SKILL.md)** — Brand setup (Step G sets initial backend)
- **[/contentforge:cf-cowork-setup](../cf-cowork-setup/SKILL.md)** — Wire Cowork + Drive before choosing the Google backend in Cowork
- **[/contentforge:cf-integrations](../cf-integrations/SKILL.md)** — Check all connector status
- **[/contentforge:cf-connect](../cf-connect/SKILL.md)** — Set up individual connectors