linkedin-studio/references/scheduling-strategy.md
Kjell Tore Guttormsen 677eab9294 feat(linkedin-studio): N13 — publiserings-slots + vacancy-varsel + WIP-roll-up + slot-default [skip-docs]
A1-10 + D-5. The queue answers "what is scheduled"; nothing answered "is the week
covered, and what is still being written". N13 makes both readable at session start.

- Publishing slots are operator config: profile/publishing-slots.json in the per-user
  data dir, schema + OPT-IN template in config/publishing-slots.template.json. The
  plugin ships no publishing times and writes the grid for nobody; absent config means
  every slot surface stays silent (same never-nag discipline as the trend/brain nudges).
  Data dir rather than the state file, which is machine-written and whose frontmatter
  reader does not parse lists.
- hooks/scripts/slots.mjs (new): one implementation behind four surfaces. Zero-dep (a
  SessionStart hook must not spawn tsx), pure core (dates in as strings, no clock),
  imported by the commands exactly as queue-manager.mjs already is — so session-start's
  vacancy warning and Step 10's slot default cannot drift apart.
- Coverage counts the short-form queue (scheduled/published; cancelled frees the slot)
  AND editions-register rows claiming that date, counted per date, so long-form and
  short-form cannot be double-booked and a two-slot day needs two posts.
- Session start gains "## Editions in Flight" (series, phase, next action, claimed slot,
  days in flight) and "## Publishing Slots" (next open slot, open count in 14 days, and
  how to fill it when the gap is <=3 days). The existing discovery nudge is untouched.
- /linkedin router + /linkedin:calendar surface the same roll-up; calendar documents the
  opt-in copy. /linkedin:newsletter Step 10 defaults to the next open slot (one
  confirmation, not an interview) and writes --slot onto the register row.
- D-5: references/scheduling-strategy.md marked "confidence: low / directional" per the
  algorithm reference's own standard, deferring to the operator's grid and their own
  analytics. Neutral example time replaces the operator-specific one in the N12 docs.

TDD: red proven first (module absent; 5 session-start behaviours failing).
hooks 140 -> 174 · test-runner 184 -> 197 (Section 16t: 13 unconditional greps incl. a
de-niche check that no publishing time is hardcoded in the slot path and no operator grid
is committed, + non-vacuity self-test; anti-erosion floor 166 -> 179).
Suites green: trends 300/0 · brain 134/0 · editions 72/0 · specifics-bank 45/0 ·
tests 35/0 · render 60/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QxvWAjte7vPcF79QeSRvRJ
2026-07-25 07:28:48 +02:00

4.9 KiB

Post Scheduling Strategy

Reference for calculating a posting schedule from weekly goal and content mix.

Epistemic status (D-5)

Confidence: low / directional for every day/time in this file — practitioner heuristic, no primary source. Same standard as references/algorithm-signals-reference.md: treat these as a hypothesis to test per account, not as a finding about your audience. LinkedIn publishes no optimal-time data, and the numbers below come from general B2B practitioner lore, not from measurement of your followers.

The authoritative grid is therefore the operator's own, in ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/profile/publishing-slots.json (schema + opt-in: config/publishing-slots.template.json). That file is what /linkedin:calendar, the session-start vacancy warning and /linkedin:newsletter Step 10 actually read. The tables here are the starting point it is seeded from, to be replaced with what /linkedin:report and /linkedin:analyze show for the account — the plugin hardcodes no publishing times.

Directional Posting Slots

Based on weekly_goal from state file (times: low / directional — test per account):

2x/week

Slot Day Time (CET) Rationale
1 Tuesday 08:30 Peak B2B engagement window
2 Thursday 12:00 Lunch-break engagement peak

3x/week (default)

Slot Day Time (CET) Rationale
1 Tuesday 08:30 Peak B2B engagement window
2 Thursday 12:00 Lunch-break engagement peak
3 Saturday 10:00 Weekend catch-up readers, less competition

4x/week

Slot Day Time (CET) Rationale
1 Monday 09:00 Week-start motivation content
2 Tuesday 08:30 Peak B2B engagement window
3 Thursday 12:00 Lunch-break engagement peak
4 Saturday 10:00 Weekend catch-up readers

5x/week

Slot Day Time (CET) Rationale
1 Monday 09:00 Week-start motivation content
2 Tuesday 08:30 Peak B2B engagement window
3 Wednesday 08:30 Mid-week authority content
4 Thursday 12:00 Lunch-break engagement peak
5 Saturday 10:00 Weekend catch-up readers

Scheduling Algorithm

When the operator has a configured grid, hooks/scripts/slots.mjs answers "what is the next free slot" deterministically (nextFreeSlot) — coverage counts both queue entries and editions-register rows, so long-form and short-form cannot be scheduled on top of each other. The steps below are the fallback for an unconfigured grid.

When assigning dates to batch-created posts:

  1. Start from next available slot after today
  2. Skip slots that already have queued posts (check queue.json)
  3. If all slots this week are taken, spill into next week
  4. Assign in slot order (earliest available first)

Slot Assignment Logic

Given: weekly_goal, today's date, existing queue entries
1. Get the slot template for this weekly_goal (tables above)
2. Find current ISO week
3. For each post to schedule:
   a. Find next available slot (date >= tomorrow, no existing queued post)
   b. Assign that date + time
   c. Mark slot as taken
4. Return list of (date, time) assignments

Format Rotation Rules

Avoid monotony by rotating formats:

  • No consecutive same format — If post N is "standard", post N+1 should be "carousel", "quick", "video", etc.
  • Suggested rotation: standard → carousel → quick → standard → video
  • Format weights: 50% standard, 20% carousel, 15% quick, 15% video

Pillar Balance Rules

Ensure coverage across expertise areas:

  • No consecutive same pillar — Enforced by topic-rotation-gate hook
  • No pillar >50% in a 14-day window — Also enforced by hook
  • Ideal distribution: Each pillar appears at least once per 2 weeks
  • When batching: Spread pillars evenly across the week

Time Zone Notes

Also low / directional, and written from one European vantage point — read the peaks below as an illustration of the shape of the question, not as your audience's hours. The operator's grid records its own IANA zone; the plugin schedules in local wall-clock time and converts nothing.

  • All times are CET (Central European Time)
  • Norwegian audience peaks: 7:30-9:00 and 11:30-13:00
  • For international audiences, 08:30 CET catches both EU morning and US east coast pre-work
  • Saturday posts perform well 09:00-11:00 CET

Queue Integration

When posts are scheduled via /linkedin:batch:

  1. Each post gets a scheduled_date and scheduled_time from this algorithm
  2. Entry is added to ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/drafts/queue.json
  3. Session-start hook shows today's scheduled posts
  4. /linkedin:calendar (publish action) marks posts as published and updates state
  5. /linkedin:calendar shows the full schedule view