## State Transitions

### Transition Rules

**Allowed Transitions**:

| From State | To State | Method |
|------------|----------|--------|
| Draft | Published | `release-create` |
| Draft | Scheduled | `release-create` with `publish_at` |
| Published | Draft (new version) | Edit operation |
| Published | Unpublished | `*-unpublish` tool |
| Published | Scheduled (unpublish) | `*-unpublish` with `unpublish_at` |
| Scheduled | Published | Automatic (at `publish_at`) |
| Scheduled | Unpublished | Automatic (at `unpublish_at`) |
| Unpublished | Published | `release-create` |
| Unpublished | Archived | `*-archive` tool |
| Archived | Draft | Restore operation |

**Forbidden Transitions**:

| From State | To State | Why |
|------------|----------|-----|
| Draft | Unpublished | Can't unpublish what was never published |
| Published | Archived | Must unpublish first |
| Archived | Published | Must restore to draft first |

### Transition Examples

**Draft → Published**:
```typescript
// 1. Create resource (draft)
const widget = await widget-definitions-create({ widgetName: "Card" });

// 2. Get publishable elements
const elements = await release-get-elements-to-publish({ siteId: 4612 });

// 3. Publish
await release-create({
  data: { widgetDefinition: [{ id: widget.id, selected: true }] }
});

// Transition: draft → published
```

**Published → Draft (New Version)**:
```typescript
// Widget is published
const published = await widget-get-custom-widgets({ siteId: 4612 });

// Edit creates new draft version
const definitions = await widget-definitions-list({ siteId: 4612 });
const widget = definitions.widget_definitions[0];
await widget-definition-update({ widgetId: widget.id, html: "Updated" });

// State: published (live) + draft (pending)
// Transition: published → published + draft
```

**Published → Unpublished**:
```typescript
// Page is published
await page-unpublish({ pageId: 456 });

// Transition: published → unpublished
```

**Unpublished → Published (Re-publish)**:
```typescript
// Page is unpublished
const elements = await release-get-elements-to-publish({ siteId: 4612 });
await release-create({
  data: { page: [{ id: 456, selected: true }] }
});

// Transition: unpublished → published
```

**Published → Archived (Pages Only)**:
```typescript
// Must unpublish first
await page-unpublish({ pageId: 456 });

// Then archive workflow
await page-archive({ pageId: 456 });

// Transition: published → unpublished → archived
```

---

## Querying Resource States

### Templates

```typescript
// Get single template with state
const template = await template-get({
  platformSlug: "fed-team",
  siteId: 4612,
  templateId: 123
});

console.log({
  published: template.published,    // boolean
  status: template.status,          // "draft" | "published"
  deletable: template.deletable     // Can it be deleted?
});

// List templates with state filter
const templates = await template-list({
  platformSlug: "fed-team",
  siteId: 4612,
  type: "custom_snippet"  // Filter by type
});

// Check if publishable
const publishable = await release-get-elements-to-publish({ siteId: 4612 });
const isPublishable = publishable.template.some(t => t.id === 123);
```

### Widget Definitions

```typescript
// Get widget with state
const widget = await widget-definition-get({
  platformSlug: "fed-team",
  siteId: 4612,
  widgetId: 123
});

console.log({
  published: widget.published,      // boolean
  read_only: widget.read_only,      // CLI widget?
  version_id: widget.version_id     // Current version
});

// Check if published (in custom widgets)
const customWidgets = await widget-get-custom-widgets({ siteId: 4612 });
const isPublished = customWidgets.custom_widgets.some(w => w.uuid === widget.uuid);

// Check if publishable
const publishable = await release-get-elements-to-publish({ siteId: 4612 });
const isPublishable = publishable.widgetDefinition.some(w => w.oid === widget.uuid);
```

### Pages

```typescript
// Get page with state
const page = await page-get({
  platformSlug: "fed-team",
  siteId: 4612,
  pageId: 456
});

console.log({
  published: page.published,        // boolean
  status: page.status,              // "draft" | "published" | "scheduled" | "unpublished"
  publish_at: page.publish_at,      // Scheduled publish
  unpublish_at: page.unpublish_at,  // Scheduled unpublish
  workflow_id: page.workflow_id     // For archiving
});

// List pages with state filter
const pages = await page-list({
  platformSlug: "fed-team",
  siteId: 4612,
  states: ["published"]  // Filter by state
});

// Check if publishable
const publishable = await release-get-elements-to-publish({ siteId: 4612 });
const isPublishable = publishable.page.some(p => p.id === 456);
```

### Menus

```typescript
// Get menu with state
const menu = await navigation-menu-get({
  platformSlug: "fed-team",
  siteId: 4612,
  menuId: 101
});

console.log({
  published: menu.published,  // boolean
  items: menu.items          // Menu items
});

// Check if publishable
const publishable = await release-get-elements-to-publish({ siteId: 4612 });
const isPublishable = publishable.menu.some(m => m.id === 101);
```

---

## Common State Issues

### Issue 1: "Resource not appearing in publishable elements"

**Symptoms**: Created/edited resource not in `release-get-elements-to-publish` results.

**Causes**:
1. Resource unchanged since last publish
2. Team review enabled, not approved yet
3. Resource already published (no pending changes)

**Solutions**:
```typescript
// Check if resource has pending changes
const template = await template-get({ templateId: 123 });
console.log(template.published);  // If true, no pending changes

// Make a change
await template-save({ templateId: 123, body: "Updated" });

// Now appears in publishable
const elements = await release-get-elements-to-publish({ siteId: 4612 });
// template with ID 123 now in elements.template
```

### Issue 2: "Widget ID not found after publishing"

**Symptoms**: Widget exists but can't be retrieved by ID after publishing.

**Cause**: Widget ID changes during publish cycle.

**Solution**: Use UUID for identification.
```typescript
// Before publish - save UUID
const widget = await widget-definition-get({ widgetId: 123 });
const uuid = widget.uuid;

// Publish
await release-create({ ... });

// After publish - find by UUID
const customWidgets = await widget-get-custom-widgets({ siteId: 4612 });
const published = customWidgets.custom_widgets.find(w => w.uuid === uuid);
console.log(published.id);  // New ID, likely different from 123
```

### Issue 3: "Page shows old widget version"

**Symptoms**: Updated widget not appearing on page.

**Cause**: Widget published but page wasn't re-published.

**Solution**: Publish page after widget changes.
```typescript
// 1. Update widget
await widget-definition-update({ widgetId: 123, html: "Updated" });

// 2. Publish widget
await release-create({ data: { widgetDefinition: [...] } });

// 3. MUST publish page
const elements = await release-get-elements-to-publish({ siteId: 4612 });
await release-create({ data: { page: [...] } });
```

### Issue 4: "Can't delete published page"

**Symptoms**: `page-delete` fails with error.

**Cause**: Published pages can't be deleted directly.

**Solution**: Unpublish → Archive → Delete workflow.
```typescript
// Step 1: Unpublish
await page-unpublish({ pageId: 456 });

// Step 2: Archive
await page-archive({ pageId: 456 });

// Step 3: Delete
await page-delete({ pageId: 456 });
```

### Issue 5: "Scheduled publish didn't execute"

**Symptoms**: Resource didn't publish at scheduled time.

**Causes**:
1. Date in past
2. Incorrect timezone
3. Platform maintenance

**Solutions**:
```typescript
// Verify date is future
const publishDate = new Date("2025-02-01T09:00:00Z");
const now = new Date();
console.log(publishDate > now);  // Must be true

// Use UTC timezone (recommended)
publish_at: "2025-02-01T09:00:00Z"  // Z = UTC

// Check scheduled releases
const publishable = await release-get-elements-to-publish({ siteId: 4612 });
// Check if scheduled in elements
```

---

## Best Practices

### 1. Always Check State Before Operations

```typescript
// ❌ Assume resource is draft
await release-create({ data: { widgetDefinition: [{ id: 123, selected: true }] } });

// ✅ Check state first
const publishable = await release-get-elements-to-publish({ siteId: 4612 });
const widget = publishable.widgetDefinition.find(w => w.id === 123);
if (!widget) {
  console.log("Widget not publishable (already published or no changes)");
} else {
  await release-create({ data: { widgetDefinition: [{ id: 123, selected: true }] } });
}
```

### 2. Use UUIDs for Widgets

```typescript
// ❌ Track by ID (changes after publish)
const widgetId = 123;

// ✅ Track by UUID (stable)
const widget = await widget-definition-get({ widgetId: 123 });
const widgetUUID = widget.uuid;  // Stable across publishes
```

### 3. Re-fetch After State Changes

```typescript
// Publish widget
await release-create({ ... });

// ❌ Use stale data
const widget = await widget-definition-get({ widgetId: 123 });  // May 404

// ✅ Re-fetch to get current state
const customWidgets = await widget-get-custom-widgets({ siteId: 4612 });
const widget = customWidgets.custom_widgets.find(w => w.name === "My Widget");
```

### 4. Follow Required Workflows

```typescript
// ❌ Skip steps
await page-delete({ pageId: 456 });  // Fails if published

// ✅ Complete workflow
await page-unpublish({ pageId: 456 });  // Step 1
await page-archive({ pageId: 456 });    // Step 2
await page-delete({ pageId: 456 });     // Step 3
```

### 5. Bundle Related Changes

```typescript
// ✅ Publish widget + page together
await release-create({
  data: {
    widgetDefinition: [{ id: 123, selected: true }],
    page: [{ id: 456, selected: true }]
  }
});
// Atomic state transition - both publish together
```

---

## Summary

**Key Takeaways**:

1. **All resources follow state machine**: Draft → Published → Unpublished → Archived
2. **Always query state** before operations
3. **Widget IDs change** during publish - use UUIDs
4. **Pages require 3-step delete**: Unpublish → Archive → Delete
5. **State transitions have rules** - some require intermediate steps
6. **Scheduled states** transition automatically at specified times
7. **Team review** adds Pending state between Draft and Published

**Quick State Reference**:

| Resource | States | Critical Tools |
|----------|--------|----------------|
| Templates | draft, published | `release-get-elements-to-publish`, `release-create`, `template-unpublish` |
| Widgets | draft, published, draft+published | `widget-definitions-list`, `widget-get-custom-widgets`, `release-create` |
| Pages | draft, published, scheduled, unpublished, archived | `page-get`, `page-unpublish`, `page-archive`, `page-delete` |
| Menus | draft, published | `navigation-menu-get`, `release-create` |

---

**Document Version**: 1.0.0
**Last Updated**: 2025-01-20
**Related Docs**: [PUBLISHING_WORKFLOW.md](PUBLISHING_WORKFLOW.md), [WIDGET_WORKFLOWS.md](WIDGET_WORKFLOWS.md)
