# Analytics → Content → Website Mapping Workflow

Use this reference when a dashboard has imported real platform analytics but the rows are not yet linked to internal content objects, prepared packages, or website companion pages.

## Goal

Turn raw analytics rows into a creator decision graph:

```text
Analytics video
  -> Content Script
  -> Content Family
  -> Prepared Package / VideoAsset
  -> Website Companion Page
  -> Next production recommendation
```

Do this before starting new upload/OAuth work if the user says the analytics are technically imported but not understandable from a creator point of view.

## Source index pattern

Build a read-only source index from every stable metadata source before asking the user to map rows manually:

- Static website candidate JSON, e.g. `data/candidates/*.json`.
- Website content markdown/frontmatter, e.g. `src/content/redflags/*.md`.
- Platform upload audits, e.g. `data/youtube_upload_audits/*.json`.
- Prepared/review package metadata, e.g. `data/post_candidates/**/review_package*.json`.
- Existing dashboard DB objects: `ExternalPost`, `PostDraft`, `VideoAsset`, `ContentScript`, `ContentFamily/Theme`, `WebsiteCompanion`.

Normalize candidate metadata into a common shape:

```text
candidate_id
package_id
slug / website_slug
title / shortTitle / working_title
hook
platform video id / Shorts URL
website content path
family/category
script text
video path if present
source type and source path
```

## Matching priority

1. Exact platform video ID — from website candidate `videoUrl`, markdown `videoUrl`, upload audit `youtube_video_id`, PostDraft external ID, or VideoAsset external ID.
2. Exact candidate ID or package ID.
3. Exact or near-exact title — analytics title, YouTube title, ContentScript title, Website title, working title.
4. Slug/keyword similarity — e.g. `small-delivery-fee`, `fake-support-number`, `mfa-prompt-without-login`.
5. Manual review drawer.

## Confidence labels

Expose creator-readable labels, not just numeric scores:

- `High — matched by YouTube video ID`
- `Medium — matched by exact title`
- `Low — similar title only`
- `Needs review — no strong match`

Backend candidate response should carry a human reason:

```json
{
  "type": "content_script",
  "id": "...",
  "title": "...",
  "family": "...",
  "package_id": "...",
  "website_slug": "...",
  "confidence": "high",
  "reason": "Matched by YouTube video ID from website candidate"
}
```

## Bulk-link rule

Only auto-link rows when all are true:

- confidence is `high`
- there is exactly one unique high-confidence target
- target resolves to a real internal family/script/package or createable equivalent

Never bulk-link medium/low matches. Leave them for manual review. Write an activity/audit event such as `analytics_auto_linked` for each auto-link.

Before linking, provide a preview with:

- video title
- views
- target family/script/package/website slug
- reason

## UI requirements

Analytics page should show mapping state at the top:

```text
41 analytics videos
18 linked
23 unlinked
0 strong matches found
[Review matches]
[Auto-link high-confidence matches]
```

For each result row show:

- Video title and views
- Linked to: Family / Script / Package
- Website guide: exists / no guide / pending / pushed
- Suggested match if not linked
- Next action

If platform analytics are usable but optional CSVs are missing, be explicit without blocking:

```text
Video-level results imported successfully.
Daily trend CSV is missing.
Chart CSV is missing.
[Add missing CSV files]
```

## Content Library aggregation

After linking, family summaries should be computed from linked analytics:

- Scripts count
- Produced videos
- Videos with analytics
- Total views
- Best video
- Average retention / retention proxy where available
- Subscriber gain/loss
- Website guide count/status
- Next recommendation

## Recommendation rules

Use linked analytics, not raw unlinked rows:

1. High-view family → suggest another variant in that family.
2. High views but low retention → faster hook / shorter setup.
3. Low views but good retention → improve packaging/title/thumbnail.
4. Strong video without website guide → prepare guide.
5. If one family has been overused recently → rotate to second-best family.

Write the recommendation in human creator language:

```text
Next recommended video:
Family: Everyday Red Flags
Idea: Another small-fee scam variant
Reason:
- “Delivery Fee” video has 41 views and is among top performers.
- Similar red-flag format already exists.
- Make a follow-up with a different payment-pressure scenario.
```

## Website pending state

If the dashboard controls a separate website repo, surface pending generated files without pushing or discarding by default:

```text
Website update pending
Guide: <title>
Status: QA passed / commit ready
Files: 3
Push status: waiting for approval
[Preview guide]
[Show diff]
[Approve website push]
[Discard website changes]
```

Approval/discard actions must require explicit user action and should be audited. Standard state is: do nothing.

## Verification checklist

- Backend tests cover website candidate, markdown, and upload audit ID matches.
- Auto-link tests prove high-confidence unique rows link and low-confidence rows remain unlinked.
- Family totals change after linking.
- Recommendation changes after linking to family/script.
- Missing optional CSVs do not block video results.
- Frontend build passes.
- API smoke checks analytics summary, video results, family summaries, recommendation, and website status.
- Git status proves no website push occurred unless explicitly approved.
