task-calendar-plan
Multi-step TASK — planning a period of content instead of writing one piece. Enter when: the merchant asks for a content plan, calendar, or schedule covering a month, a season, or any other stretch of time; the merchant asks to redo, extend, or rebalance a plan you already laid out. Do NOT enter: a request for one specific article or post, even a long one (that is task-content-publish); deciding WHAT to promote — campaign ideas, promotion strategy, what to push (that is task-promo-plan); greetings or small talk; setting up, auto-generating, calibrating, or re-scanning Brand IQ (that is task-brand-iq-setup); asking to change a setting, connect or disconnect a channel, or edit their account (they do that themselves in the Setting Hub). Read this skill's full body BEFORE your first action — it carries the node graph you must report progress against, one node at a time.
Generated from tasks/calendar-plan/task.yaml by /task-authoring.
Do not edit here — edit the definition and regenerate.
name: task-calendar-plan description: >- Multi-step TASK — planning a period of content instead of writing one piece. Enter when: the merchant asks for a content plan, calendar, or schedule covering a month, a season, or any other stretch of time; the merchant asks to redo, extend, or rebalance a plan you already laid out. Do NOT enter: a request for one specific article or post, even a long one (that is task-content-publish); deciding WHAT to promote — campaign ideas, promotion strategy, what to push (that is task-promo-plan); greetings or small talk; setting up, auto-generating, calibrating, or re-scanning Brand IQ (that is task-brand-iq-setup); asking to change a setting, connect or disconnect a channel, or edit their account (they do that themselves in the Setting Hub). Read this skill's full body BEFORE your first action — it carries the node graph you must report progress against, one node at a time. kind: task task_name: "Content calendar plan" label_template: "<period> content plan" nodes:
- id: scope
label: "Setting up the plan"
purpose: "Get the plan configured and confirmed — pop the schedule card, and when the merchant is reworking an existing plan, read that plan back first so the card starts from what is actually scheduled."
advance_by: user
expects:
- "the confirmed schedule-card configuration — window, frequency, slots, products, keywords, channels — arriving as one message" tools:
- yepairag_marketing__REQUEST_CONTENT_CALENDAR
- yepairag_marketing__GET_CONTENT_CALENDAR next:
- to: schedule when: "the merchant confirmed the schedule card and their configuration arrived as a message"
- id: schedule
label: "Committing your calendar"
purpose: "Commit the confirmed plan as dated rows in one call — with a schedule card the server commits the card's own saved rows, each with its date, time, channels, topic keywords, 1 to 3 real products and its own writing angle — then move straight on to writing them."
advance_by: agent
tools:
- yepairag_marketing__CREATE_CONTENT_CALENDAR next:
- to: generate when: "the calendar rows are committed"
- to: scope when: "the merchant changes the configuration mid-flight — a different window, frequency or setup — so the schedule card must be raised again"
- id: generate
label: "Writing your posts"
purpose: "Write every committed row's channel versions in this same conversation — one piece at a time, in the order the server hands them over, each from its channel's writer skill — so the batch fills in on the Task Center and the calendar while the merchant watches."
advance_by: agent
artifact: { type: content_draft }
route: marketing-hub/calendar
tools:
- yepairag_marketing__CREATE_CONTENT_DRAFT tags: [internal, task, marketing, requires-yepai-tools] license: MIT-0
Task — Content calendar plan
Flow
flowchart TD
scope("Setting up the plan")
schedule["Committing your calendar"]
generate["Writing your posts"]
scope --> schedule
schedule --> generate
schedule -.-> scope
classDef terminal stroke-width:3px
class generate terminal
Rounded = waiting on the merchant · square = you are working · dashed = going back · bold border = terminal.
scope
Setting up the plan — Get the plan configured and confirmed — pop the schedule card, and when the merchant is reworking an existing plan, read that plan back first so the card starts from what is actually scheduled.
- The card collects; you do not. Call
yepairag_marketing__REQUEST_CONTENT_CALENDARonce per request and stop — the card gathers the window, frequency, time slots, products (1–3), keywords per channel, campaign and channels, and the merchant confirms INSIDE it. Do not ask for any of those in prose, before or alongside the card: the card is the question. If the tool answersduplicate: true, this turn already raised it — stop, and never re-raise it in the same turn; an unconfirmed card from an earlier turn holds nothing, so a later message asking for a plan, the same request word for word included, is a new request and gets a new card (the app hides the old one), never a reply pointing the merchant back at the old one. - Reworking an existing plan starts from the plan, not from memory. When the entry
was "redo / extend / rebalance my plan", call
yepairag_marketing__GET_CONTENT_CALENDARFIRST and speak only about rows it returned. Reciting last time's plan from conversation memory produces a confident copy of a plan that may have been dragged around in the Hub since — the calendar the merchant sees is the truth, not the chat history. - The confirmed configuration arrives as ONE ordinary message. Everything in it is decided — re-asking any field it answers is the measured card-then-interrogate failure this workspace already banned for the brief. Most of it describes the batch as a whole, but the message may also carry a "Per-post overrides" block naming individual posts by number and slot: those lines replace the shared setup for exactly those posts, field by field, and every post it does not name keeps the shared setup untouched.
- The Quick setup card (
REQUEST_CONTENT_BRIEF) belongs totask-content-publishand collects ONE piece's brief. Never raise it here — it cannot express a period, and a merchant who fills it will have asked for the wrong thing.
schedule
Committing your calendar — Commit the confirmed plan as dated rows in one call — with a schedule card the server commits the card's own saved rows, each with its date, time, channels, topic keywords, 1 to 3 real products and its own writing angle — then move straight on to writing them.
- One confirmation, one commit. Call
CREATE_CONTENT_CALENDARexactly once per confirmed card. When the confirmation says the slots are already saved with this chat, the server commits the card's own rows — each customised post on its own settings, no row for a slot the merchant removed — so pass noposts: any you send are ignored. Only a plan typed without a card passesposts(window × frequency = N rows, each with date, time, channels, topic keywords and 1–3 real products), and then a post the merchant customised carries its own values into its own row, the shared setup fills only the rest, and a post whose channels are none gets no row. Never call it speculatively, and never split one confirmation into several commits — the Hub would show a plan in fragments. - The memory is applied by the server, at commit.
CREATE_CONTENT_CALENDARreads this store's verdicts itself and writes them onto the rows it commits, and itscognition_appliedlines describe exactly those rows. Do not callGET_CONTENT_COGNITIONhere and say nothing about the memory in this turn; quotecognition_appliedonly when the merchant later asks why the plan looks the way it does, and never describe a memory-driven change it does not list. - Every row already has its own angle — the server's. Each committed row carries
angle(andtheme_hintwhen the memory steered it), assigned so that rows sharing products get different angles (past a dozen such rows an angle repeats, never on the next row). Never assign, change or re-derive an angle;topic_keywordsstays exactly what the card sent. - Dates come from the card, not from your arithmetic. With a confirmed card the dates are already saved with this chat and the server commits them — never re-derive them (re-deriving is how past dates end up on the calendar). Only for a plan typed without a card: expand the merchant's weekday+time slots across the window, skip any date already in the past, and if a slot falls on a date that does not exist in the window, drop that row — do not invent a nearby day.
- The commit is a checkpoint, not the finish. After it succeeds, move straight to
generateand say NOTHING — the commit puts the batch progress bar on screen, the tool result's own contract says to say nothing, and the app hides any text you add to that turn. Do not announce the commit, and do not paste the schedule as a table into the chat; the progress bar says how far along it is and the calendar page is where they review it. - The tool refusing is the plan being wrong, not the tool being broken. A validation error lists every problem at once (date format, channels, product count). Fix the rows and call again — unless it says the card's saved plan could not be read: then raise the schedule card again so the merchant re-confirms, and never build the rows yourself. Either way, do not tell the merchant it failed, and do not downgrade to describing the plan in prose as if it had been committed.
generate
Writing your posts — Write every committed row's channel versions in this same conversation — one piece at a time, in the order the server hands them over, each from its channel's writer skill — so the batch fills in on the Task Center and the calendar while the merchant watches.
- Write the piece the server hands you, one at a time. The commit result's
batch.nextis the first; every draft result'sbatch.nextis the one after. CallCREATE_CONTENT_DRAFTfor exactly that row and channel — blog rows a full article, Instagram and Facebook rows that channel's format — with that row's products, keyword picks and campaign, written to itsangle(on itstheme_hinttheme when it has one). The server files it on its row and groups the row's channels: pass notask_idand nopiece_id. Abatch.nextwhoseangleis null is a row from an older plan: write it to itskeywords. - The picture rides the draft, one piece at a time. On the row's first format
(
batch.next.row_first) pass ahero_sceneshowing the SCENE of that piece — its angle, not a product shot. The server draws the hero WITH the draft, grounds it in the row's product photos, and puts it on every card of that row by itself (hero_image: "drawn"in the result). Never callGENERATE_IMAGEfor a row's hero — a hero drawn outside its draft cannot tell which piece it belongs to; right after a draft the server answersalready_drawnwithout drawing, and once that freshness lapses the call draws a duplicate the merchant is billed for. Never reuse one row's scene for another. The 2026-09-03 words-first lesson stands in this form: the scene is written WITH the copy it illustrates, never before it (pictures drawn before the copy matched the subject in 7 of 24 cases; with it, 8 of 8). - Channel style comes from the writer skills, same as a single piece — read once per
turn, not once per piece. Read
writer-blog/writer-instagram/writer-facebookthe FIRST time this turn writes for that channel, and write the turn's later pieces for it from what you read; between pieces re-read nothing — not the writer skill, nothumanizer, nottask-content-publish. A batch is not a licence to write channel-less copy, and it is not a reason to reload the same rules either: every read stays in the turn, and per-piece re-reads pushed one segment past the runtime's context budget three times (dev 2026-10-23:history_trimmed×3 in a 4-piece segment, budget 76,800 tokens). - The batch echo is your anti-sameness instrument. Each accepted draft's result
carries
batch_so_far(titles and openings of everything this batch already produced): write the next piece with a different opening device — a moment of use, one concrete product detail, before-and-after, the offer itself, or the problem the reader has, stated plainly; never an invented dialogue or an invented statistic — and a different structure from ALL of them. Atoo_similarrejection names the piece you collided with — rewrite from a genuinely different angle; the one retry ships regardless, so make it count. - A failed piece never stops the batch, and is never yours to report. If a draft is
refused, fix the arguments and call once more; if
batch.nexthas moved on, the server has marked that piece failed with its reason — followbatch.next. Never name failures or offer a retry in text: the completion card lists every failed piece with its own Retry. - The chat stays quiet while you write. Drafts land as cards and on the calendar; do not paste article text into the conversation, and do not narrate each piece ("now writing #4...") — the Task Center is the progress surface.
- When
batch.nextis empty orbatch.end_turnis true, end the turn with no text. The server continues the batch in a new turn of this same conversation until every row is written or marked failed, and the completion card reports the outcome from the calendar itself — the app hides any text you add to a turn that ends a stretch of the batch.
How a task runs
This part is the same for every task; only the nodes differ.
1 — Entering the task. Report the first node before doing anything else. That report is what opens the run: it tells the backend a task has started and gives the merchant their first progress line.
2 — Entering each node. Report it the moment you enter, before doing that node's work. One report per node. A single turn usually passes through several nodes with no input from the merchant in between — reporting as you go is what lets them watch the work happen instead of staring at nothing until you finish. A node whose work is a single tool call still gets its report first.
3 — Reaching the end. A terminal node is one with no way out except back to itself. Reporting it completes the task — the backend works that out from the graph, so there is nothing extra for you to send.
4 — After completion. A completed task is not sealed. If the merchant later asks to change something, report the node that owns it again and the task is simply unfinished again. Never open a second task for a change to work you already did.
How to report
yepairag_task__REPORT_TASK_STEP
task: "calendar-plan"
node: "<the node you just entered>"
- Two fields, both read straight off the graph above. The step's label, its number, the total and whether the task just completed are filled in by the backend — you cannot get them wrong because you do not send them.
- The reply carries
legal_next: the only nodes you may report next. ok: falsemeans nothing was recorded and the run has not moved. Correct the node id and report again rather than carrying on as if it had been accepted.- Never restate any of it to the merchant. It drives a progress bar they are already looking at.
Download
ZIP package — ready to use
Skill Info
- Creator
- Yepai
- License
- MIT-0
- Downloads
- 0
- Published
- Oct 26, 2026
- Updated
- Oct 29, 2026