Workflow

Distribution = der zentrale Schreibort. Alles Writing landet hier (Scripts, Essays, Notes, FAQ), wird hier verfeinert, wird per Git → CF auto-deployed, wird via Apple-Cal sichtbar.

Three places

PlaceURLPurpose
Editor SPAhttps://a-friend-distribution.pages.dev/Capture · edit · stage · reorder · ship. Auth-gated.
Read pageshttps://a-friend-distribution.pages.dev/<Section>/<slug>Quartz-rendered pages — was Daniel-Cal verlinkt.
Apple Calendarwebcal://a-friend-distribution.pages.dev/calendar.ics (master) + 6 per-format feedsEine VEVENT pro Scripts-Entry. Refresh alle 15min.

Four verbs

1. Capture

Via SPA: + New → fill date, hook/title, body. Save → GitHub commit via /api/entry.

Via Claude-Skill (lokal): Skill schreibt direkt nach Distribution/<Section>/<slug>.md. Skill-Liste:

SkillDefault-SectionTrigger
afriend-distributionScripts”draft a script”, “neue Folge”, podcast-clip + topic
afriend-script-poolScripts”skript pool”, Readwise-URL, 11-gate voice-safety
compose-deseltrusEssays (oder beliebig — sag’s der Skill)Gedankenstrom für long-form / Substack-essay
afriend-content-extractionNotes/Doctrinevirality-canon, content-extraction-doctrine

Lokale Skill-Writes laufen über Daniel’s Git: git add → commit → push → CF rebuild → live.

2. Edit / reorder

Click row → side-panel → contenteditable hook+body, meta-chips für date/time/format/stage/channels. Save → commit.

Drag-drop innerhalb gleicher Stage = Date-swap zwischen zwei Entries (canonical sort = by date).

Filter-rows nur in Scripts: Stage + Format. Group-toggle by stage / by format (persistiert).

3. Stage

Click stage-chip → popover → next stage. Backward needs confirm. Stages:

idea → scripted → ready-to-capture → captured → cut → published

(Essays haben state: draft → edit → scheduled → published — kommt im SPA-meta-row falls fm.state gesetzt.)

4. Ship

Auto (jetzt aktiv): jeder commit → CF Pages rebuilds → ~2min → live. ICS frisch → Apple Cal pulled binnen 15min. Du fierst nichts.

Fallback bei Bedarf: npm --prefix Distribution run deploy (manual via wrangler).

Check: npx wrangler pages project list → Git-Provider = GitHub ✓.

Sections

SectionFolderFrontmatter shapeUse
ScriptsDistribution/Backlog/date, time, format(A1-C1), stage, hook, channels, slugShort-form video scripts → social channels
EssaysDistribution/Essays/date, title, state, channel(substack/blog/…), slug, tagsLong-form essays für Substack etc.
NotesDistribution/Notes/date, title, slug, tagsInternal thinking, free-form
FAQDistribution/FAQ/per FAQ-templateIndiegogo FAQ

Slug-shape:

  • Scripts: YYYY-MM-DD-<format-code>-<slug> (z.B. 2026-05-28-b1-episode-2-fragmentation)
  • Essays/Notes/FAQ: YYYY-MM-DD-<slug> (flat)

Format canon (Scripts only)

Source: Strategy/a-friend-content-system-v1.md. Always full name, never abbrev allein.

CodeFull nameCadence
A1B-Roll Organic + UGC Scenarios2-3 / week
A2Best Friend’s Content Creator POV1-2 / week
A3Carousels1-2 / week
B11M Challenge1 / week (Tue)
B2Reaction / Educational1 / week (Thu)
C1Collab-Posting (Substack)as it comes

Apple-Cal subscribe-URLs

Master (alles):

webcal://a-friend-distribution.pages.dev/calendar.ics

Per-format (eigene Farbe pro Cal):

webcal://a-friend-distribution.pages.dev/calendar-a1.ics
webcal://a-friend-distribution.pages.dev/calendar-a2.ics
webcal://a-friend-distribution.pages.dev/calendar-a3.ics
webcal://a-friend-distribution.pages.dev/calendar-b1.ics
webcal://a-friend-distribution.pages.dev/calendar-b2.ics
webcal://a-friend-distribution.pages.dev/calendar-c1.ics

Apple Cal → File → New Calendar Subscription → URL einfügen → name. Jeder Feed = ein Calendar = eine Farbe. Day-view zeigt automatisch die Format-Farbstreifen.

Auth

RoleKeyCapability
Founder (edit)EDIT_SECRET env, local backup /tmp/afd-edit-secret.txtVolle CRUD über alle Sections
Daniel (read)READ_SECRET env, local backup /tmp/afd-daniel-secret.txtList + open only

Paste-in localStorage, sent as Bearer-Header. clears. localStorage persistiert across browser-quit per spec — keine clientseitige TTL.

Stale-secret detection + renew flow

Functions discriminieren jetzt zwischen no-secret (kein Token gesendet) und stale-secret (Token präsentiert aber matched nicht mehr env.EDIT_SECRET / env.READ_SECRET). Antwort-shape:

{
  "ok": false,
  "error": "stale-secret",
  "renew_at": "https://dash.cloudflare.com/?to=/:account/pages/view/a-friend-distribution/settings/environment-variables",
  "hint": "Token does not match current EDIT_SECRET. Renew via the URL and paste the new value."
}

SPA-side: bei stale-secret bleibt der gespeicherte Token IM localStorage (kein wipe), Login-screen zeigt inline klickbare “Renew at Cloudflare” CTA mit dem renew_at URL. Bei no-secret wird localStorage geleert + neutraler “Paste your access token” prompt.

Wann passiert “stale-secret”?

  • Du hast den CF env-var EDIT_SECRET rotiert (e.g., via npx wrangler pages secret put EDIT_SECRET)
  • Du hast in einem anderen Browser den Token gewechselt und syncthing hat alte Backups
  • Browser hat localStorage von älterer Session

Renewal-flow:

  1. CF Dashboard → Pages → a-friend-distribution → Settings → Environment Variables
  2. Edit EDIT_SECRET / READ_SECRET → set new value → Save
  3. Update local backup: echo -n "<new-secret>" > /tmp/afd-edit-secret.txt
  4. SPA: paste new value in login-input → save → localStorage updated

Local backup files at /tmp/afd-*.txt sind survival nach Mac-restart NICHT garantiert (/tmp wipes). Für dauerhaft: kopiere zu ~/.config/afd/secrets/ oder 1Password.

Safety / Remote setup

LayerStatusFailover
Local repo~/Projects/afriend-business/Syncthing → iMac mirror
GitHubdeseltrus/afriend-business (private)Source of truth. Re-clone if local lost.
CF Pagesa-friend-distribution.pages.devBuilt from GitHub on push. Wegwerf-build artifacts.
SecretsCF env (wrangler pages secret list)Local backup unter /tmp/afd-*.txt
CalendarGenerated from Backlog/. Repo stale → ICS staleRun npm run deploy to force rebuild

Failure recovery: if CF goes wrong, repo + secrets give complete rebuild path. Manual wrangler pages deploy works any time.

Repo hygiene

Total repo size: ~21GB working, ~4GB git pack. Mass = source-assets/, _assets/, content-extraction/, design-system PNGs.

Going forward:

  • Generated/derivative PNGs (campaign exports, proposal renders) → gitignore wenn regenerierbar
  • Source-PNGs / hand-crafted assets → keep in git
  • Large media (video stills, raw footage) → consider git-LFS or external R2 bucket
  • node_modules/, _quartz/public/ already gitignored

No urgent action — repo functional. Optimize when push-times become painful.

What lives where

Distribution/
├─ _admin-src/index.html       ← Editor SPA (dashboard at /)
├─ _functions-src/api/
│   ├─ entries.js              ← GET list per section
│   ├─ entry.js                ← GET/PUT/POST/DELETE one entry (sections: scripts|essays|notes|faq)
│   └─ advance.js              ← POST stage advance (scripts only)
├─ _scripts/
│   ├─ prebuild.mjs            ← sync-doctrine → sync-scripts → emit-ics → build-faq
│   ├─ emit-ics.mjs            ← Backlog/*.md → calendar.ics + 6 per-format feeds
│   ├─ build-faq.mjs           ← FAQ/*.md → Quartz-list page
│   ├─ sync-doctrine.mjs       ← mirror ~/.claude/skills/… into Doctrine/
│   ├─ sync-scripts.mjs        ← mirror Onari-substrate scripts/ into Scripts/
│   └─ build.mjs               ← quartz build + admin SPA overlay + functions copy
├─ Backlog/<slug>.md           ← Scripts entries (source of truth)
├─ Essays/<slug>.md            ← Long-form (Substack etc.)
├─ Notes/<slug>.md             ← Internal thinking
├─ FAQ/<slug>.md               ← Indiegogo FAQ
├─ Strategy/                   ← Constitution, pillars, values (Quartz-rendered)
├─ Doctrine/                   ← Auto-mirror, read-only
├─ Scripts/                    ← Auto-mirror, read-only
├─ Inbox/, Daily-Compile/      ← Local Obsidian only (Quartz-ignored)
└─ calendar*.ics               ← 7 feeds emitted on each build

Decision rules

  1. One output, one hook/title, one body. Multi-version only on explicit ask.
  2. English forever für composed content. Founder-quotes verbatim in <details>Original</details>.
  3. No emoji in entries.
  4. No fabrication. Audience / channels / tags / pillar-links nur wenn user es gibt.
  5. Stage moves forward unless explicit override.
  6. No skill auto-chain. Skills triggern nur auf user-explicit-invoke.
  7. Skill writes target the right section. Scripts → Backlog/, Essays → Essays/, etc.