## TL;DR

Keep one flat log, one row per published piece per channel, written by the same pipeline run that publishes it. Columns: date, piece title, channel, status, canonical URL, published URL, notes. A log written at publish time never drifts; a log reconstructed later always does.

```text
how to keep a publishing log across channels
```

## Use this when

- You publish the same content to several channels (dev.to, Substack, Blogger, Medium, forums)
- You need to answer "did this piece go out on X yet" without checking each platform
- You want a single source of truth for cross-posting and canonical URLs
- Your publishing runs unattended and you review results later

## Not for this skill when

- You need traffic or conversion analytics per channel (different skill)
- You only publish to one channel (that platform's dashboard is enough)
- You want a full content calendar or editorial workflow

## Steps

1. Pick the store. A markdown file, a CSV, or a sheet; one row per piece per channel. Expected: a single file you can grep or filter.

```
Columns: date | piece | channel | status | canonical_url | published_url | notes
```

2. Define the status values up front. Published, scheduled, failed, held, skipped. Expected: every row has a status you can filter on later.

```
status values: published, scheduled, failed, held, skipped
```

3. Write the row at publish time. The publishing script appends the row in the same run that posts the piece, before moving on. Expected: the log never lags reality.

```
Pseudo: publish(piece, channel) -> append row -> next channel.
If publish fails, write status=failed with the error in notes.
```

4. Record the canonical URL on every row. Cross-posted copies need the original linked. Expected: you can audit canonical coverage across channels.

```
canonical_url: the one true URL for this piece.
published_url: where this channel copy lives.
```

5. Review weekly. Filter for failed and held rows, retry or reschedule them. Expected: nothing silently dies in a failed state.

```
Weekly pass: show rows where status in (failed, held) from the last 7 days.
```

## Variant phrasings

### Tracking what I published where

Same setup: one row per piece per channel, written at publish time.

### Content distribution log template

Use the column set above as the template; add a "campaign" column if you run themed batches.

### How to know which channels a piece went out on

Filter the log by piece title or canonical URL; every channel row shows up.

## Why it happens

Publishing is a fan-out operation and each channel has its own dashboard, so without a central log the state of "did it ship everywhere" only exists in your memory. Memory doesnt scale across runs, and a log written after the fact is reconstruction, which misses failures. Writing the row inside the publish step makes the log a side effect of the work, not a separate chore.

## Edge cases / pitfalls

- Concurrent publishers writing to the same file can clobber rows; merge-on-read or use an append-only log.
- Scheduled-but-never-sent rows need a reconciliation pass, or they rot.
- Keep failed rows with the error text in notes; "failed" alone isnt actionable.
- If a channel copy gets deleted later, flip the row to deleted instead of removing it, so the history stays honest.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_JyX9nPZKnZwvyIW41QfObA
