## Publishing Strategies

### Strategy 1: Incremental Publishing (Recommended)

**When**: Small changes, frequent deploys, multiple developers.

**Approach**: Publish each resource as it's ready.

```typescript
// 1. Update widget
await widget-definition-update({ widgetId: 123, html: "..." });

// 2. Publish widget immediately
const elements = await release-get-elements-to-publish({ siteId: 4612 });
await release-create({
  data: { widgetDefinition: [{ id: 123, selected: true }] }
});

// 3. Later: update page
await page-update({ pageId: 456, ... });

// 4. Publish page separately
const elements2 = await release-get-elements-to-publish({ siteId: 4612 });
await release-create({
  data: { page: [{ id: 456, selected: true }] }
});
```

**Pros**:
- Fast feedback loop
- Minimal blast radius if issues occur
- Easy to roll back individual changes

**Cons**:
- More publish operations
- Potential for temporary inconsistencies

### Strategy 2: Bundled Publishing

**When**: Related changes that must go live together.

**Approach**: Modify all resources, then publish as one release.

```typescript
// 1. Update widget
await widget-definition-update({ widgetId: 123, html: "..." });

// 2. Update page that uses widget
await page-update({ pageId: 456, ... });

// 3. Update snippet referenced by widget
await template-save({ templateId: 789, body: "..." });

// 4. Publish ALL together
const elements = await release-get-elements-to-publish({ siteId: 4612 });
await release-create({
  data: {
    widgetDefinition: [{ id: 123, selected: true }],
    page: [{ id: 456, selected: true }],
    template: [{ id: 789, selected: true }]
  }
});
```

**Pros**:
- Atomic updates (all or nothing)
- No temporary inconsistencies
- Single publish operation

**Cons**:
- Larger blast radius if issues occur
- Slower feedback loop

### Strategy 3: Staged Publishing

**When**: Production sites with QA environments.

**Approach**: Publish to staging, test, then publish to production.

```typescript
// STAGING
await release-create({
  platformSlug: "staging",
  siteId: 4612,
  data: { ... }
});

// Test in staging environment
// ...verify...

// PRODUCTION (after QA approval)
await release-create({
  platformSlug: "production",
  siteId: 5623,
  data: { ... }  // Same elements
});
```

---

## Scheduled Publishing

### Future Publishing

**Use Case**: Launch at specific time (marketing campaigns, announcements).

```typescript
await release-create({
  platformSlug: "fed-team",
  siteId: 4612,
  scheduled: true,
  publish_at: "2025-02-01T09:00:00Z",  // 9 AM UTC on Feb 1
  data: {
    page: [{ id: 456, selected: true }],
    widgetDefinition: [{ id: 123, selected: true }]
  }
});

// Elements will publish automatically at scheduled time
```

### Temporary Publishing

**Use Case**: Time-limited content (promotions, events).

```typescript
await release-create({
  platformSlug: "fed-team",
  siteId: 4612,
  scheduled: true,
  publish_at: "2025-02-01T00:00:00Z",   // Start of promo
  unpublish_at: "2025-02-14T23:59:59Z", // End of promo
  data: {
    page: [{ id: 789, selected: true }]  // Valentine's promo page
  }
});

// Page goes live on Feb 1, automatically unpublishes on Feb 14
```

### Important Notes

- `publish_at` must be in the future
- `unpublish_at` must be after `publish_at`
- Use ISO 8601 format with timezone (UTC recommended)
- Scheduled releases execute even if Modyo admin is offline

---

## Unpublishing

### Manual Unpublish

Use type-specific unpublish tools:

```typescript
// Unpublish page
await page-unpublish({
  platformSlug: "fed-team",
  siteId: 4612,
  pageId: 456
});

// Unpublish template
await template-unpublish({
  platformSlug: "fed-team",
  siteId: 4612,
  templateId: 789
});
```

**Notes**:
- Widget definitions don't have unpublish tool (use releases)
- Menus don't have unpublish tool (use releases)
- Unpublishing makes resource unavailable but doesn't delete it

### Scheduled Unpublish

```typescript
// Schedule unpublish for page
await page-unpublish({
  platformSlug: "fed-team",
  siteId: 4612,
  pageId: 456,
  layout_page: {
    unpublish_at: "2025-03-01T00:00:00Z"
  }
});
```

---

## Common Workflows

### Workflow 1: New Widget + Page

**Goal**: Create widget and add to new page.

```typescript
// 1. Create widget definition (draft)
const widget = await widget-definitions-create({
  platformSlug: "fed-team",
  siteId: 4612,
  widgetName: "Product Card"
});

// 2. Update widget code
await widget-definition-update({
  widgetId: widget.id,
  html: "<div>{{ product.name }}</div>",
  css: ".card { padding: 1rem; }",
  js: ""
});

// 3. Publish widget
const elements1 = await release-get-elements-to-publish({ siteId: 4612 });
const widgetElement = elements1.widgetDefinition.find(w => w.oid === widget.uuid);
await release-create({
  data: { widgetDefinition: [{ id: widgetElement.id, selected: true }] }
});

// 4. Re-fetch to get published widget UUID
const customWidgets = await widget-get-custom-widgets({ siteId: 4612 });
const publishedWidget = customWidgets.custom_widgets.find(w => w.name === "Product Card");

// 5. Create page with widget
const page = await page-create({
  name: "Products",
  path: "products",
  type: "default",
  grid_type: "full_grid"
});

// 6. Add widget to page
await page-add-widgets({
  pageId: page.id,
  widgets: [{
    type: "custom_widget",
    definition_uuid: publishedWidget.uuid,
    column: 0,
    position: 0
  }]
});

// 7. Publish page
const elements2 = await release-get-elements-to-publish({ siteId: 4612 });
const pageElement = elements2.page.find(p => p.id === page.id);
await release-create({
  data: { page: [{ id: pageElement.id, selected: true }] }
});

// Complete: Widget live, page live at /products
```

### Workflow 2: Update Published Widget

**Goal**: Edit widget that's already published and used on pages.

```typescript
// 1. Find widget by name in widget definitions (NOT custom widgets)
const definitions = await widget-definitions-list({ siteId: 4612, query: "Product Card" });
const widget = definitions.widget_definitions.find(w => w.name === "Product Card");

// 2. Update widget code
await widget-definition-update({
  widgetId: widget.id,
  html: "<div class='updated'>{{ product.name }}</div>",
  css: ".updated { padding: 2rem; }",
  js: ""
});

// 3. Publish changes
const elements = await release-get-elements-to-publish({ siteId: 4612 });
const widgetElement = elements.widgetDefinition.find(w => w.oid === widget.uuid);
await release-create({
  data: { widgetDefinition: [{ id: widgetElement.id, selected: true }] }
});

// Widget updates immediately on all pages using it
```

### Workflow 3: Theme Update (Multiple Templates)

**Goal**: Update site-wide CSS across multiple templates.

```typescript
// 1. Update root CSS template
const rootTemplate = await template-list({
  siteId: 4612,
  type: "css"
}).then(list => list.templates.find(t => t.name === "root"));

await template-save({
  templateId: rootTemplate.id,
  body: `:root {
    --primary: #ff0000;  /* Updated */
  }`
});

// 2. Update base CSS template
const baseTemplate = await template-list({
  siteId: 4612,
  type: "css"
}).then(list => list.templates.find(t => t.name === "base"));

await template-save({
  templateId: baseTemplate.id,
  body: `body {
    color: var(--primary);  /* Uses updated root */
  }`
});

// 3. Publish both templates together
const elements = await release-get-elements-to-publish({ siteId: 4612 });
const rootElement = elements.template.find(t => t.name === "root");
const baseElement = elements.template.find(t => t.name === "base");

await release-create({
  data: {
    template: [
      { id: rootElement.id, selected: true },
      { id: baseElement.id, selected: true }
    ]
  }
});

// Theme updates across entire site immediately
```

---

## Best Practices

### 1. Always Query Before Publishing

**❌ Don't assume IDs**:
```typescript
// Wrong - widget ID may have changed
await release-create({
  data: { widgetDefinition: [{ id: 86885, selected: true }] }
});
```

**✅ Always query first**:
```typescript
const elements = await release-get-elements-to-publish({ siteId: 4612 });
const widget = elements.widgetDefinition.find(w => w.name === "My Widget");
await release-create({
  data: { widgetDefinition: [{ id: widget.id, selected: true }] }
});
```

### 2. Bundle Related Changes

**❌ Separate publishes for related changes**:
```typescript
// Widget relies on snippet
await release-create({ data: { widgetDefinition: [...] } });
// Widget live but snippet not - broken temporarily
await release-create({ data: { template: [...] } });
```

**✅ Publish together**:
```typescript
await release-create({
  data: {
    widgetDefinition: [...],
    template: [...]  // Atomic update
  }
});
```

### 3. Re-fetch After Publishing

**Widget IDs change after publishing**:
```typescript
// Before publish
const widgetDef = await widget-definition-get({ widgetId: 123 });

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

// After publish - get updated ID
const customWidgets = await widget-get-custom-widgets({ siteId: 4612 });
const widget = customWidgets.custom_widgets.find(w => w.uuid === widgetDef.uuid);
// Use widget.uuid for stable identification
```

### 4. Verify Elements Exist

**Before publishing, confirm element is in publishable list**:
```typescript
const elements = await release-get-elements-to-publish({ siteId: 4612 });

// Check widget exists and is publishable
const widgetExists = elements.widgetDefinition.some(w => w.id === 123);
if (!widgetExists) {
  throw new Error("Widget 123 not found in publishable elements");
}

await release-create({ ... });
```

### 5. Use Descriptive Release Selections

**Document what's being published**:
```typescript
const elements = await release-get-elements-to-publish({ siteId: 4612 });

// Find by name/path for clarity
const productWidget = elements.widgetDefinition.find(w => w.name === "Product Card");
const productsPage = elements.page.find(p => p.path === "products");
const productSnippet = elements.template.find(t => t.name === "product_card");

await release-create({
  data: {
    widgetDefinition: [{ id: productWidget.id, selected: true }],
    page: [{ id: productsPage.id, selected: true }],
    template: [{ id: productSnippet.id, selected: true }]
  }
});
// Clear what's being published: product feature bundle
```

### 6. Test in Staging First

**Avoid publishing directly to production**:
```typescript
// 1. Staging
await release-create({
  platformSlug: "staging",
  siteId: 4612,
  data: { ... }
});

// 2. Manual testing in staging
// 3. Automated tests (optional)

// 4. Production (after validation)
await release-create({
  platformSlug: "production",
  siteId: 5623,
  data: { ... }  // Exact same elements
});
```

---

## Common Mistakes

### Mistake 1: Using Stale IDs

**Problem**: Widget/template IDs can change, causing publish failures.

```typescript
// ❌ Wrong - hardcoded ID
await release-create({
  data: { widgetDefinition: [{ id: 86885, selected: true }] }
});
// Error: Widget 86885 not found (ID changed)
```

**Solution**: Always query first.

```typescript
// ✅ Correct
const elements = await release-get-elements-to-publish({ siteId: 4612 });
const widget = elements.widgetDefinition.find(w => w.name === "My Widget");
await release-create({
  data: { widgetDefinition: [{ id: widget.id, selected: true }] }
});
```

### Mistake 2: Forgetting to Publish Widget

**Problem**: Adding unpublished widget to page fails silently.

```typescript
// Create widget (draft)
const widget = await widget-definitions-create({ widgetName: "Card" });

// ❌ Try to add to page without publishing
await page-add-widgets({
  widgets: [{ definition_uuid: widget.uuid }]
});
// Widget won't appear - not published yet
```

**Solution**: Always publish widget before adding to page.

```typescript
// ✅ Correct workflow
const widget = await widget-definitions-create({ widgetName: "Card" });

// Publish first
const elements = await release-get-elements-to-publish({ siteId: 4612 });
await release-create({ data: { widgetDefinition: [...] } });

// Re-fetch to get published UUID
const customWidgets = await widget-get-custom-widgets({ siteId: 4612 });
const published = customWidgets.custom_widgets.find(w => w.name === "Card");

// Now add to page
await page-add-widgets({
  widgets: [{ definition_uuid: published.uuid }]
});
```

### Mistake 3: Publishing Incomplete Changes

**Problem**: Publishing widget before updating code.

```typescript
// Create widget
const widget = await widget-definitions-create({ widgetName: "Card" });

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

// Try to update after publishing
await widget-definition-update({ widgetId: widget.id, html: "..." });
// Widget ID may have changed after publish
```

**Solution**: Update code before publishing.

```typescript
// ✅ Correct order
const widget = await widget-definitions-create({ widgetName: "Card" });

// Update code first
await widget-definition-update({
  widgetId: widget.id,
  html: "<div>Content</div>",
  css: ".card { }",
  js: ""
});

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

### Mistake 4: Not Verifying Element Selection

**Problem**: Assuming element will be in publishable list.

```typescript
const elements = await release-get-elements-to-publish({ siteId: 4612 });

// ❌ No verification
await release-create({
  data: { widgetDefinition: [{ id: 123, selected: true }] }
});
// May fail if ID 123 doesn't exist or isn't publishable
```

**Solution**: Always verify before publishing.

```typescript
const elements = await release-get-elements-to-publish({ siteId: 4612 });

// ✅ Verify element exists
const widget = elements.widgetDefinition.find(w => w.id === 123);
if (!widget) {
  throw new Error("Widget 123 not found or not publishable");
}

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

### Mistake 5: Mixed Publishing Strategies

**Problem**: Inconsistent approach causes confusion.

```typescript
// Sometimes publish immediately
await widget-definition-update({ ... });
await release-create({ ... });

// Sometimes batch
await widget-definition-update({ ... });
await page-update({ ... });
// ... wait ...
await release-create({ ... });
```

**Solution**: Choose one strategy per project/team.

---

## Troubleshooting

### Issue: "Widget not found in publishable elements"

**Cause**: Widget hasn't been modified since last publish, or is already published.

**Solution**:
1. Check if widget needs changes: `widget-definition-get({ widgetId })`
2. Make a change (even whitespace): `widget-definition-update({ ... })`
3. Query again: `release-get-elements-to-publish({ ... })`

### Issue: "Widget ID changed after publishing"

**Cause**: Modyo assigns new IDs to widgets during publish cycle.

**Solution**: Use UUIDs for identification, not IDs.
```typescript
// Track by UUID (stable)
const widget = await widget-definition-get({ widgetId: 123 });
const uuid = widget.uuid;

// After publish, find by UUID
const customWidgets = await widget-get-custom-widgets({ siteId: 4612 });
const published = customWidgets.custom_widgets.find(w => w.uuid === uuid);
// Use published.id for further operations
```

### Issue: "Page not showing updated widget"

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

**Solution**: Publish page after widget update.
```typescript
// 1. Update widget
await widget-definition-update({ ... });

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

// 3. MUST publish page too
const elements = await release-get-elements-to-publish({ siteId: 4612 });
const page = elements.page.find(p => p.name === "Products");
await release-create({ data: { page: [{ id: page.id, selected: true }] } });
```

### Issue: "Template changes not reflected on site"

**Cause**: Template not published, or browser cache.

**Solution**:
1. Verify template is published
2. Clear browser cache (Ctrl+Shift+R)
3. Check CDN cache for CSS/JS templates (may take minutes)

### Issue: "Scheduled publish didn't occur"

**Cause**: Incorrect timezone, date in past, or Modyo maintenance.

**Solution**:
1. Verify date is in future: `new Date('2025-02-01T09:00:00Z') > new Date()`
2. Use UTC timezone (avoid local timezones)
3. Check Modyo platform status

### Issue: "Can't unpublish home page"

**Cause**: Modyo prevents unpublishing home pages via API.

**Solution**:
1. Create new page
2. Set as home in site settings (Modyo UI)
3. Unpublish old home page

### Issue: "Empty publishable elements list"

**Cause**: No pending changes, or all changes already published.

**Solution**:
1. Make a change to any resource
2. Query again
3. If using team review, ensure changes are approved

---

## Summary

**Key Takeaways**:

1. **Always use two-step process**: Query publishable elements, then select and publish
2. **Verify element exists** before including in release
3. **Bundle related changes** for atomic updates
4. **Re-fetch after publishing** to get updated IDs/UUIDs
5. **Choose consistent strategy**: Incremental, bundled, or staged
6. **Test in staging** before production
7. **Use UUIDs** for stable widget identification

**Quick Reference**:

| Action | Tools |
|--------|-------|
| Get publishable elements | `release-get-elements-to-publish` |
| Publish selected elements | `release-create` |
| Schedule publish | `release-create` with `publish_at` |
| Unpublish page | `page-unpublish` |
| Unpublish template | `template-unpublish` |

---

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