Zest Design Doc
Zest
Content Pipeline for LinkedIn
Capture sparks when they happen. Batch with everything pre-loaded.
The Problem
The raw material for LinkedIn content is generated organically through real conversations, client work, industry observations, and personal experiences. But the generation and the production happen in completely different contexts, and the handoff between them is where content ideas die.
Three failure modes compound on each other:
Capture failure: Story sparks, interesting angles, and "I should post about this" moments happen during random Claude chats, client sessions, or daily life. There is no system to catch them. They stay in chat history or memory and decay.
Retrieval failure: When a batch session starts, the first 30+ minutes go to archaeology — scrolling through old chats, trying to remember what was interesting last week, re-explaining context that already existed somewhere. The energy that should go to writing goes to remembering.
Composition failure: The five named posting days are not independent. The week is a unit. Monday sets a tone, Wednesday builds on it, Friday can call back to earlier posts. Drafting posts in isolation misses opportunities for the week to feel cohesive. When inspiration is low, there is no structured way to surface seeds that could grow into posts.
The Solution
Zest is a two-part system: a lightweight capture tool for grabbing content sparks in the moment, and a batch production environment that pre-loads everything so writing sessions start with a full menu instead of a blank page.
Part 1: Spark Capture
A spark is any raw material that could become a LinkedIn post. It could be a story fragment, an observation, a reaction to something, a question someone asked, a lesson from client work, or a link to something worth commenting on.
Sparks are captured with minimal friction and maximum context. The goal is to spend 15 seconds capturing, not 5 minutes documenting.
Part 2: Batch Production
A batch session produces 1-2 weeks of LinkedIn content. It starts with a pre-loaded context package: all unprocessed sparks, the current content calendar state, recent post performance (when available), and the voice profile. The session works through the week in order, day by day, composing the week as a unit.
Spark Capture
What Gets Captured
| Spark Type | Example | Typical Source |
|---|---|---|
| war_story | "The Kubernetes Docker EE vs AWS standoff" | Random Claude chat, memory |
| observation | "Noticed recruiters are posting about title inflation" | LinkedIn browsing |
| client_echo | "Kirk said something about feeling invisible to leadership" | Client session (anonymized) |
| reaction | "Hot take on that viral post about 10x engineers" | LinkedIn feed |
| question | "Someone in DMs asked how I knew when to push back on a project" | DM conversation |
| seed | "Something about how the best career move I made looked like a lateral" | Shower thought, walk, etc. |
| news | "AWS just announced a major pricing change" | Industry news |
| callback | "This connects to Monday's post about ownership" | Mid-week realization |
Spark Schema
---
type: war_story | observation | client_echo | reaction | question | seed | news | callback
captured_at: 2026-03-20T10:30:00
raw: "The thing Kirk said about feeling like he was doing senior work but nobody noticed — that's the exact title gap story"
day_fit: [monday, thursday]
energy: high | medium | low
used: false
used_in: null
tags: [title_gap, visibility, kirk]
---
Field notes:
rawis the brain dump. No formatting required. Stream of consciousness is fine. This is the spark, not the post.day_fitis optional. If you immediately know this is a Story Day (Thursday) topic, tag it. If not, leave it blank and Zest will suggest fits during batch.energyis how excited you are about this idea right now. High energy sparks get prioritized in batch sessions. This is a gut-feel field, not a score.usedandused_inget populated after a batch session turns this spark into a draft.
Capture Methods
Method 1: Mobile capture via Pulp (primary)
This is the default. Most sparks happen when you are away from your computer — walking, at 1909, between DMs, during conversations. Three variants of the iOS Shortcut cover different situations:
"Save Spark" Shortcut (typing)
- Opens a text input (or grabs clipboard)
- Shows a quick menu for spark type (war_story, observation, seed, reaction, etc.) — optional, can skip and tag later
- Shows a quick menu for energy level (high, medium, low) — optional, defaults to medium
- Auto-generates filename from timestamp (spark-2026-03-20-143000.md)
- Commits to
learnwithoj-internal/linkedin/sparks/via Pulp - Shows confirmation notification
Total time: under 20 seconds.
"Quick Spark" Shortcut (fastest typing) Skips the type and energy menus entirely. Just text in, commit, done. You tag metadata during batch. This is for when you are mid-conversation and can't afford even 10 extra seconds.
"Voice Spark" Shortcut (hands-free, for driving/walking)
- Uses the iOS "Dictate Text" action — you speak your thought and it transcribes on-device
- Skips all menus (no tapping while driving)
- Auto-tags as
type: seedandenergy: medium(defaults, refined during batch) - Commits transcription to sparks directory via Pulp
- Audio confirmation via Siri: "Spark saved"
Can be triggered entirely through Siri: "Hey Siri, Voice Spark" → start talking → done. Zero screen interaction. Eyes on the road, spark captured.
If the built-in iOS dictation is not sufficient for longer stream-of-consciousness captures (2-3 minutes of rambling), a future upgrade path is to use the "Record Audio" action and transcribe via the Whisper API before committing. But start with native dictation — it handles conversational speech well and since sparks are raw brain dumps, transcription errors barely matter. The idea is what counts, not the wording.
Method 2: CLI (from terminal)
zest spark
Opens editor. Paste or type the raw thought. Save. Zest prompts for type and optional day_fit. Writes spark file to learnwithoj-internal/linkedin/sparks/. Best for when you are at your laptop and have a moment to add more context.
Method 3: Quick capture (one-liner from terminal)
zest spark "Kirk said something about feeling invisible to leadership" --type client_echo --energy high
No editor needed. Good for fast capture when you are in the middle of a Claude Code session or terminal work.
Method 4: Chat extraction (future)
Zest scans recent Claude chat history (with your permission) and surfaces moments that look like content sparks. "You mentioned the Kubernetes story in a chat on Tuesday. Want to capture that as a spark?" This is the ambitious version and would require the past chats search capability.
Batch Production
Pre-Batch Context Package
When you start a batch session, Zest assembles a context package:
- Unprocessed sparks: All sparks with
used: false, sorted by energy level, grouped by day_fit - Content calendar: What has been posted recently, what is scheduled, what days are open for the target weeks
- Series tracker: Active series with episode count and what is next (e.g., ALW build-in-public, week 8 of 12, next episode is Wednesday)
- Voice profile: Your LinkedIn post voice (separate from DM voice in Squeeze)
- Recent performance: Which recent posts got the most engagement, comments, DMs (manual input until Community Management API is approved)
- Posting rules: The five named days with their themes and constraints
The Five Named Days
| Day | Name | Theme | Content Direction |
|---|---|---|---|
| Monday | Career Ops | Career strategy and positioning | Tactical advice for software and infrastructure engineers navigating their careers. Operational, not inspirational. |
| Tuesday | Hands-On Tuesday | Technical content | Walkthroughs, code, architecture decisions, debugging stories. Show the work. |
| Wednesday | The Shop Floor | Build in public, behind the scenes | What is happening with Learn with O.J., tools being built, lessons from running the business. Can be overridden by active series (ALW). |
| Thursday | Story Day | Narrative, war stories | Personal stories from 20+ years in engineering. The human side. Longer format. |
| Friday | Happy Friday | Light, fun, community | Callbacks to the week, celebrations, funny observations. Not formulaic — sometimes a callback to Monday, sometimes standalone. |
Active Series Tracking
Some content runs as a series with a fixed number of episodes. Zest tracks these separately from one-off posts.
---
series_name: ALW Build in Public
day: wednesday
total_episodes: 12
current_episode: 8
start_date: 2026-01-22
description: Weekly updates on the 1909 Applied Learning Workshop sprint
overrides_theme: true
---
When a series is active, Zest reserves that day's slot and does not suggest other content for it. When the series ends, the day reverts to its default theme.
Batch Session Flow
zest batch --weeks 2
-
Load context: Zest assembles the context package and displays a summary. "You have 14 unprocessed sparks. 3 are high energy. You have an ALW episode due Wednesday of week 1. Friday of week 1 has no sparks yet."
-
Menu phase: Zest presents the sparks grouped by suggested day fit. You pick which sparks to develop for which days. Zest fills gaps by asking seed questions based on your war story library, recent client themes, or industry topics. "You haven't posted a Hands-On Tuesday in two weeks. Any recent technical work worth sharing? What about the Azure Logic App password rotation project with Kirk?"
-
Composition phase: Work through the week in order. Monday first, then Tuesday, and so on. For each day, Zest provides the spark context, the voice profile, and any relevant constraints (series episode, callback opportunity, etc.). You collaborate on the draft.
-
Week review: After all five days are drafted, Zest shows the week as a unit. Flag any thematic overlap, missed callback opportunities, or tonal monotony. "Monday and Thursday both lead with frustration. Consider softening one of them."
-
Repeat for week 2: The second week accounts for what week 1 covers to avoid repetition.
-
Export: Final drafts are saved to
learnwithoj-internal/linkedin/drafts/with scheduling metadata. Sparks used in drafts get markedused: truewith a reference to the draft file.
Gap Filling: The Seed Engine
When sparks are thin for a particular day, Zest does not just say "you have nothing for Tuesday." It prompts with specific seeds drawn from:
- War story library: "You mentioned the Amex intern development story in a past conversation. That could be a Thursday Story Day post."
- Client patterns: "Two of your recent leads mentioned feeling stuck after being passed over for promotion. That is a common thread worth a Monday Career Ops post." (Anonymized, obviously.)
- Industry hooks: "AWS re:Invent announcements are this week. Any takes for Hands-On Tuesday?"
- Contrarian angles: "The 'just leetcode harder' advice is trending again. You have a different perspective on interview prep that could be a good Monday post."
- Callback prompts: "Monday's post about ownership got 12 comments. Worth a Friday callback?"
The seed engine is not generating post ideas from nothing. It is connecting dots between things you have already said, done, or experienced and surfacing them at the right moment.
Draft Schema
---
week_of: 2026-03-24
day: monday
name: Career Ops
title: "The title gap is a systems problem"
status: draft | ready | scheduled | published
spark_refs: [spark-2026-03-18-001, spark-2026-03-15-003]
series: null
word_count: 247
reviewed: false
scheduled_date: 2026-03-24
published_url: null
---
[Post content here]
Draft Directory Structure
linkedin/
sparks/
spark-2026-03-18-001.md
spark-2026-03-18-002.md
spark-2026-03-20-001.md
drafts/
2026-w13/
monday-career-ops.md
tuesday-hands-on.md
wednesday-shop-floor.md
thursday-story-day.md
friday-happy-friday.md
2026-w14/
monday-career-ops.md
...
series/
alw-build-in-public.md
published/
2026-w12/
...
Organizing by ISO week number keeps things clean and makes it easy to see how far ahead you are at a glance.
Breaking News and Schedule Disruption
When something timely happens that you want to post about:
Option 1: Replace. Swap the timely post into that day's slot and bump the original draft to next week or back to the spark pile. Zest tracks the swap.
Option 2: Double post. Post both, but be aware that LinkedIn's algorithm tends to split engagement across same-day posts. Zest flags this tradeoff but does not prevent it.
Option 3: Reactive thread. Save the hot take as a spark and develop it properly for the next available day where it fits thematically. A well-crafted post two days later often outperforms a rushed hot take on the day.
Zest defaults to recommending Option 1 (replace and bump) unless the timely topic has a very short shelf life.
Integration Points
Squeeze → Zest
When Squeeze captures a DM conversation that contains content-worthy material (a client question that many engineers probably have, a story that emerged naturally), it can flag the conversation as a spark source. Zest picks up the flag and adds a spark to the queue with the relevant context pre-filled.
Zest → Pulp (dependency)
Pulp is a prerequisite for Zest's mobile capture. The "Save Spark" and "Quick Spark" iOS Shortcuts are Pulp shortcuts with pre-configured repo and directory settings. This means Pulp Phase 1 (core endpoint) must ship before Zest Phase 0 can work on mobile. Drafted posts are also committed to the repo via Pulp from your phone.
Zest → LinkedIn API (future)
When the Community Management API is approved, Zest can push scheduled posts directly to LinkedIn. Until then, drafts are copy-pasted manually from the repo. The draft schema already includes scheduled_date to support this transition.
Zest → Client Scout (future)
Content performance data feeds back into Client Scout's understanding of which topics attract which prospect types. If a post about the title gap generates three DMs from senior engineer aspirants, that signal strengthens the coaching client prospect profile.
Implementation Phases
Phase 0: Spark Capture Only
Build Pulp first (prerequisite). Then build the "Save Spark" and "Quick Spark" iOS Shortcuts that commit to learnwithoj-internal/linkedin/sparks/. Mobile capture is the primary path and ships before the CLI. Also build zest spark CLI command for laptop use. Start capturing immediately. Estimated time: one session for Pulp, one session for the Shortcuts and CLI.
Phase 1: Batch Context Loader
Build zest batch that assembles the context package from unprocessed sparks, calendar state, and series tracker. Output is a structured brief you paste into a Claude batch session. The batch session itself is still a regular Claude chat — Zest just pre-loads it. Estimated time: one session.
Phase 2: Seed Engine
Add the gap-filling prompts. When sparks are thin for a day, Zest generates seed questions from the war story library and client patterns. This is the feature that solves the "I don't have anything to post about" problem. Estimated time: one to two sessions.
Phase 3: Week Composition
Add the week review step that checks for thematic overlap, callback opportunities, and tonal balance. Add draft export to the weekly directory structure. Mark sparks as used. Estimated time: one session.
Phase 4: Series Management
Build the series tracker for active series like ALW. Automatic slot reservation, episode counting, and revert-to-default when series completes. Estimated time: quick addition.
Phase 5: Performance Feedback (requires Community Management API)
Pull post analytics and feed them back into the seed engine and batch context. Which topics drive engagement, which day/topic combos work best, which posts generated DMs (cross-referencing with Squeeze data). This is the compound interest layer.
Open Questions
- Should sparks support images or screenshots, or just text? Sometimes a screenshot of a LinkedIn post you want to react to is the fastest capture method.
- How should the war story library be structured? Is it a separate file that Zest reads, or are war stories just sparks with
type: war_storythat never expire? - Should Zest track engagement manually (you input likes/comments/DMs after publishing) until the API is available, or skip performance tracking entirely until Phase 5?
- What is the right batch cadence? Two weeks at a time seems to be working. Should Zest enforce a minimum buffer (e.g., always stay two weeks ahead)?
- How do we handle placeholder posts? Sometimes you know you need a Tuesday post but don't have the content yet. Should Zest support a "placeholder with theme" that gets filled in later?
- Should the seed engine have access to your Tier 1 commenting targets' recent posts? Knowing what Gergely Orosz or Jordan Cutler posted this week could surface reaction or counterpoint angles.
- Is there value in Zest analyzing your published post archive to identify which topics and formats you have not covered recently? "You haven't posted a Hands-On Tuesday about Kubernetes in 6 weeks."
- Should draft files include the full post text or just a structured outline that you flesh out in a Claude session?
- For the ALW series and future series, should Zest auto-generate a series retrospective post when the series ends? "12 weeks of building in public, here is what I learned."
Zest | Learn with O.J. | learnwithoj.com