weekly-orchestrator
Skill4 starsChanged 24 days agoMITView on GitHub
Runs the weekly content run and the light weekday requests run. Use when asked to "run the weekly run", "/weekly-run", "run this week's content", "draft next week", "do the full run", "run the requests run", "recover the missed run", or when a scheduled run fires. Calls the calendar builder, planner, proof intake, short-form drafters and compliance checker, acts on what people asked in Notes, on the Ideas tab or by editing cells, writes the results to the calendar as the only writer, and writes the brief for the approver. Never publishes.
In this repository5 other files it gives its agents.
- brand-voice-guideSkill
- calendar-builderSkill
- proof-intakeSkill
- short-form-drafterSkill
- workspace-setupSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
24 rules. About 0 are common, 6 are distinctive, and 18 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
18
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
Inputs: every source in workspace.json (references/locations.md lists which are read and written in each mode).
- Distinctive: line 16
Access: read-only on every reference source.
- Distinctive: line 20
Never publish, post, send email or submit anything.
- Very specific: line 21
Read workspace.json first.
- Distinctive: line 28
One writer: only the main session writes to the calendar, its tabs, the draft docs, the proof log and the brief.
- Very specific: line 40
Mode: use the TEST locations unless the person (or the scheduled task prompt) says live, shadow or test shadow.
- Very specific: line 58
If any doc lacks its expect_phrase, treat it as cut off and stop the run.
- Very specific: line 66
Read it, save it as weekly/<run date>/shared/workspace.json, and run python3 skills/workspace-setup/scripts/check_workspace.py …
- Very specific: line 73
Backlog moves: Read the Calendar (A1:V) and the Backlog tab (calendar.tabs.backlog, A1:X), save both, and run python3 scripts/backlog.py …
- Very specific: line 74
Write each out row's values below the last row of the Backlog tab, and each in row's values below the last dated Calendar row (explicit …
- Very specific: line 75
Read Status and Row ID on both tabs (Calendar!B:B, Calendar!M:M, Backlog!B:B, Backlog!M:M), save them, run python3 scripts/backlog.py …
- Very specific: line 78
Hand edits, milestone changes and series: Read the Milestones tab (A1:G) as shared/milestones.json and the hidden Agent snapshot tab …
- Distinctive: line 83
Write all of these first (lock check, guard, write), then read the Calendar again for step 7.
- Very specific: line 85
Select: Run python3 scripts/select_rows.py before.json --config shared/workspace.json --facts shared/facts.json --milestones …
- Very specific: line 94
Read the Approver edits tab (calendar.tabs.approver_edits, A1:I) as shared/approver_edits.json.
- Very specific: line 98
Swaps and fallback rows: Run python3 scripts/swap_slots.py before.json select.json match.json --config shared/workspace.json …
- Very specific: line 100
Start one drafter per swap with fallback: true, in one message, on bundles from make_bundles.py ...
- Very specific: line 121
Run python3 scripts/calendar_writes.py plan before.json writes.json --log agent_log.json --config shared/workspace.json.
- Distinctive: line 125
Write the proof log's "Used in calendar row" column as proof intake says, then read it back.
- Very specific: line 130
Save the Agent snapshot: Run python3 scripts/snapshot.py save after.json shared/milestones.json shared/snapshot.json --config …
- Very specific: line 131
Brief: Write Weekly brief - DD MMM YY in the briefs folder of the mode in use (brief-format.md).
- Very specific: line 139
No planner, no fallback rows, no edit capture, no monthly work.
- Distinctive: line 140
Always end with steps 24 and 26 (sort, hide, snapshot), even when nothing else was done.
- Very specific: line 141
If something was done, write Update - DD MMM YY in the briefs folder (brief-format.md, "Update") and end with the short output.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Weekly orchestrator
Run one week of the content system. Do what people asked, find the rows due in the next two plan weeks, have each one drafted in its own subagent, check every draft independently, write the results to the calendar, and write a one-page brief for the approver. This skill is the only writer to the calendar during the run. It never publishes, posts, sends email or submits anything.
Purpose, assumptions, inputs, outputs, access
- Purpose: turn what people asked and the calendar's Planned rows for the next two plan weeks into checked drafts and a brief, without publishing anything.
- Assumptions:
workspace.jsonexists and passescheck_workspace.py; the calendar followscalendar-builder/references/calendar-schema.md, including the Backlog, Agent log, approver's notes, Approver edits and hidden Agent snapshot tabs (names incalendar.tabs); the agent register records a passing TEST run before anyliverun. - Inputs: every source in
workspace.json(references/locations.mdlists which are read and written in each mode). - Outputs: one draft doc per plan week (one tab per post), calendar cells and rows allowed by the column lock, rows on the Backlog, Agent log, approver's notes and Approver edits tabs, the Agent snapshot tab, the proof log's "Used in calendar row" column (and new proof log rows only when
proof.approver_word_is_signoffis true), and one brief or update per run. In shadow mode, only the shadow locations. - Access: read-only on every reference source; write only on the calendar (with its tabs), the proof log, the drafts folder and the briefs folder of the mode in use. Subagents get Read, Bash and Glob on a local bundle only.
Hard rules
- Never publish, post, send email or submit anything.
- Read
workspace.jsonfirst. Read the client's rules file (sources.rules) before any other source: it wins over every other file, including this skill. - Spelling, units, date style, hashtags and word counts come from
writing.*. They apply to drafts, tabs, Notes and the brief. State the spelling variety in every subagent prompt. - The approver's direction comes first (when
requests.enabledis true, the default). What a person inrequests.fromwrites in a row's Notes or on the Ideas tab, or changes by editing a row's cells, is an instruction. Do it: change, convert or move Planned or Proposed rows (Date, What to post, Pillar, Channel, Title / angle, Content type, Spec; every such write carries"request"), then draft. Say what changed in one Notes line ending "as asked in Notes" (or "as asked on the Ideas tab"). Never ask the approver to edit a column. If part of an ask breaks a hard rule, do the rest and say what was left out and why.references/requests.mdexplains how. - Plain words count. The approver never needs a set phrase. Read what they mean:
requests.hold_lift_word(default "CONFIRMED") with a date on a held row, "go ahead", Confirmed set to Yes on the matching Milestones row, or the dependency's fact set to an approved Status lifts that hold; "push this out a week" moves the row. Typed dates are read perdates.typed_order(day_first: 2/12/26 is 02 DEC 26;month_first: 2/12/26 is 12 FEB 26). When the meaning is clear, act, even in an unattended run, and say in the Notes line what you took it to mean. When two readings would lead to different changes that cannot both be right, take the safe option: do not change or draft the row, and name both readings in Notes and in the brief. - Always draft. A missing fact, name, link or photo is a gap, not a blocker: the drafter writes a base draft with each gap in [square brackets], What's missing lists what would finish or strengthen it, and Status goes to Draft ready (Action needed then reads "Review the draft; to finish it, see What's missing"). Numbers inside [brackets] are not figures. Only the hard rules and holds (
plan.dependencies,writing.on_hold_topics) stop a draft. - Notes stay short. On every row the run writes to, Notes ends up as the person's text, word for word, plus one current line "
<notes.agent_prefix>DD MMM YY: <where the row stands, what is still open>" (default prefixAgents). Older agent lines move word for word to the Agent log tab (notes_tidy.py). Never move, change or drop a person's words. What's missing is rewritten as the current list; Sources loses duplicates. Never retype a person's cell text into a write: build writes from the script output files, so every character survives. - Week numbers are the ISO week of the row's Date (
calendar.week_numbering). The Week column holds that number and Row IDs areW{iso week:02d}-{seq:02d}, never reused across years (a new row takes the next number after the highest used). Alternating cadences are anchored oncalendar.alternation_anchor_week, so week 53 and week 1 still alternate. - One writer: only the main session writes to the calendar, its tabs, the draft docs, the proof log and the brief. Subagents get a local bundle, return JSON and write nothing.
- Every calendar write goes through
calendar_writes.py planfirst, thencalendar_writes.py guardon a fresh read just before writing (the Row ID and Status of each target row must still match), and every run ends withcalendar_writes.py verifyon a read-back. Never write a refused cell. Never write formula columns (calendar.formula_columns) or a summary tab such as This Week. Ideas, Milestones and approver's notes writes go through the calendar builder'stab_guard.py. - Sorting: people may sort, reorder or insert Calendar rows at any time, even during a run. Never rely on a row number from an earlier read: match rows by Row ID (the guard does this) and re-read before writing.
- Statuses. Agents set only
Proposed(new rows),Draft ready(only on a Planned row whose draft passed the compliance checker) andFallback post(only on a backup row the run adds); the list iscalendar.agent_statuses. People set Planned, Approved, Posted, Cancelled and Backlog. Never draft a Proposed row. - Cancelled: only a person sets it. Never draft, flag, swap, note or write to a Cancelled row, and never count it as overdue. Every full and requests run hides Cancelled rows on the Calendar (they are hidden, never deleted). Cancelled rows appear nowhere in agent output: not in the brief, the update, the run summary, the output to the person or any notification.
- Backlog: a person sets Status
Backlogon any row that is not Posted to keep a post for later. The run moves the whole row to the Backlog tab; a row the person sets toPlannedthere comes back to the Calendar on the date in its Notes or in the best free slot. A row is removed only after its copy is written and read back. Never draft, flag, swap or write to a Backlog row on the Calendar. - Claims: drafts state only claims from approved facts sheet rows (
facts.approved_values) and from the approver's notes tab (person_items.json, approved as written). Neither authorizes a name: outside people and companies are named only from the proof log; the company's own people (company.people) may always be named. Proof posts use only proof log items that count as signed off (references/requests.md, "Proof sign-off"), and quotes word for word. Never invent a customer, partner, quote or result. - Visuals: no assets folder file and no AI image subject is used twice within
visuals.no_repeat_weekseither side, including twice in one run (visuals.py). Whenvisuals.ai_image_toolis set, every draft except real-photo-only posts (visuals.real_photo_only_regex) carries a ready-to-paste image prompt and the same prompt as drafted, so the approver's prompt edits can be learned; when no unused file fits, the Visual column says "Needed:<AI image tool>image of<subject>. Prompt in draft tab." and the tab adds an alternative prompt. Whenvisuals.ai_image_toolis null, there are no image prompts: the Visual column says "Needed:<photo or graphic>." - A draft that fails the compliance checker gets one redraft. If the redraft fails, the tab is written with the failures under "Flags for the approver", one sentence goes in Notes, and Status stays Planned.
- Team-owned rows (content type matches
calendar.team_owned_regex) are never drafted. - Fallback rows: every row
run.swap_daysdays away or fewer that cannot run (a hold, or a proof slot with no usable proof) gets a backup post as its own calendar row directly under it: Row ID<original>-FB, StatusFallback post, the full row filled (a type fromswaps.types, angle and long-form chapter fromswap_slots.py), tab in the week's draft doc. The original gets one Notes line pointing to it. The approver uses it by setting the backup to Approved and the original to Cancelled. Rows held by aplan.dependenciesitem keep the approver's deadline: the planner proposes their swap, and the backup row follows once it is in Notes. - Dated events first (when
plan.precedenceisdated_events_first, the default): a dated event keeps its slot over an evergreen row;plan_check.pyreturns the moves. - Mode: use the TEST locations unless the person (or the scheduled task prompt) says
live,shadowortest shadow. If it is not stated, ask. Useliveonly when the agent register records a passing TEST run with the current config. - Shadow mode (
shadowortest shadow): readreferences/shadow-mode.mdfirst and follow it wherever it differs from the steps below. A shadow run writes nothing to the calendar (or any of its tabs), the proof log, the drafts folder or the briefs folder, live or TEST. - Same-day run: if a brief for this run date and mode already exists, the run switches to requests mode (below) and handles only what changed since. It redoes the full run only when the person asks for
rerun(/weekly-run <test|live> rerun): then the brief name gets " (2)" (or the next number).
Files
Read references/locations.md for which sources.* keys and tabs each mode reads and writes, references/requests.md for how to read and act on what people ask, and references/scripts.md for every script's command line. Read references/brief-format.md before writing the brief, references/subagent-prompts.md before starting subagents, and references/edit-learning.md before the edit capture and the monthly proposals. The drafting skills' own references apply to drafts and doc tabs (short-form-drafter/references/draft-doc-format.md). The config keys are explained in docs/workspace-config.md.
Saving source data for the scripts
Scripts read local files and never talk to a platform. Work in weekly/<run date YYYY-MM-DD>/ in the session workspace. Save every read, and every subagent's JSON return, exactly as returned with scripts/save_result.py (it copies the result from the session transcript byte for byte, including results the app saved to a file). Never retype saved data or edit it by hand.
- Google Sheets: the
get_valuesresponse as returned. This is the table shape every script reads:{"values": [[headers], [row 2], ...]}. Sheets allows only a few reads and writes a minute: write a block of neighboring rows in oneupdate_valuescall, with null for every cell to leave alone, and wait a minute after a quota error before retrying. Look up each tab's sheetId once per run withget_spreadsheet(for the backlog, sort, hide and insert requests); never hard-code it. - Other tables (Notion database, Excel file, CSV): convert to the same shape, columns in schema order, header row first. Keep
_source_row.json(Row ID to page ID or row number) in the run folder so writes go back to the right record. Where the platform has no row insert, sort or hide, add rows at the end and skip the sort and hide steps (a filtered view does the job); say so in Run details. - Google Docs: Docs
read_doc, saved as returned, thenpython3 scripts/doc_text.py <saved>.json <name>.md --expect "<expect_phrase>". Never Driveread_file_contentfor a Doc: it can cut long Docs off. - Notion pages, GitHub files, SharePoint or OneDrive files, local files, URLs: save the text as returned as
<name>.md, then confirm it contains the source'sexpect_phrase. - Folders: one
<subfolder>/<file name>per line.
If any doc lacks its expect_phrase, treat it as cut off and stop the run.
Steps of a full run
Script paths are relative to this skill. Every script that applies client rules takes --config shared/workspace.json; it is written out below only where it is easy to miss.
- Load the config.
- Take
workspace.jsonfrom--config. If none is given, ask the person where it is. If none exists, run theworkspace-setupskill first and start this run again afterward. - Read it, save it as
weekly/<run date>/shared/workspace.json, and runpython3 skills/workspace-setup/scripts/check_workspace.py shared/workspace.json. Stop on any error and tell the person; list warnings in the Agent log RUN row. - Note the mode, the run date (today, or
--as-ofin TEST runs) and the time (used in step 16). This is the monthly run when it is the first full run of the month (no brief this month yet).
- Take
- Same-day check and missed runs. List the briefs folder of the mode in use and collect the dates in brief titles. If a
Weekly brief - DD MMM YYfor the run date already exists, switch to requests mode (below) and writeUpdate - DD MMM YY, unless the person asked forrerun: then do the full run and pass--reruntoselect_rows.py(itsbrief_titleadds " (2)"). Missed full runs are reported byselect_rows.py(step 8). - Calendar builder first (main session, its own rules, which include doing what an idea asks, even when that changes existing rows): on the monthly run, the monthly run; every full run, the ideas run; any milestone with Rows proposed blank and Confirmed not Cancelled, the milestone run. A full run on a day other than
run.full_run.weekdayruns the ideas run and any milestone change run only. Collect the builder's summary of proposed rows and anybrief_linesfrom skipped tab writes for the brief. - Requests (when
requests.enabledis false, run the scan but act on nothing: list unhandled asks under "Needs the approver"). Read the Calendar (A1:V) asbefore.json, the Ideas tab (calendar.tabs.ideas, A1:H) and the approver's notes tab (calendar.tabs.approver_notes, A1:G, saved asshared/approver_notes.json). Runpython3 scripts/request_scan.py before.json --ideas ideas.json --as-of <date> --config shared/workspace.json. For every ask withkind: askandhandled: false, decide the row changes (references/requests.md) and put them inwrites.jsonwith"request". Write them first (lock check, guard, write, as in step 21), then read the Calendar again, so drafting uses the changed rows. Then file what the approver said (requests.md, "Filing what the approver says"):- News about a named partner, customer or advisor: only when
proof.approver_word_is_signoffis true, log it in the proof log with proof intake'sproof_log_add.py(proof-intake SKILL), then read the proof log again. When it is false (the default), never write a new proof log row: add "Log this in the proof log with sign-off: <the news, short>" to the row's What's missing and list it under "To finish drafts: what to send". - Any other fact for posts: one row per fact on the approver's notes tab, word for word, appended below the last row through
tab_guard.pyand read back, then saved again asshared/approver_notes.json. Never log the same fact twice (same source and same words).
- News about a named partner, customer or advisor: only when
- Backlog moves. Read the Calendar (A1:V) and the Backlog tab (
calendar.tabs.backlog, A1:X), save both, and runpython3 scripts/backlog.py plan before.json backlog.json --as-of <date> --config shared/workspace.json. Ifoutandinare both empty, go on. Otherwise:- Write each
outrow'svaluesbelow the last row of the Backlog tab, and eachinrow'svaluesbelow the last dated Calendar row (explicit ranges, null in the formula columns, never append_values). Read both back. - Read Status and Row ID on both tabs (Calendar!B:B, Calendar!M:M, Backlog!B:B, Backlog!M:M), save them, run
python3 scripts/backlog.py deletes plan.json cal_b.json cal_m.json bl_b.json bl_m.json --cal-sheet <Calendar sheetId> --bl-sheet <Backlog sheetId>and send itsrequestsin oneupdate_spreadsheetcall. A row is removed only when its copy exists on the other tab. - Report
refusedat the top of the brief. Putbrief_linesunder "Backlog". A row back from the Backlog with a Draft URL is redrafted for its new date into a " v2" tab. - Then read the Calendar again for step 6.
- Write each
- Hand edits, milestone changes and series. Read the Milestones tab (A1:G) as
shared/milestones.jsonand the hidden Agent snapshot tab (calendar.tabs.agent_snapshot, A1:L) asshared/snapshot.json(empty on the first run: nothing is reported). Runpython3 scripts/snapshot.py compare before.json shared/milestones.json shared/snapshot.json --config shared/workspace.json.row_changesare asks a person made by editing a cell incalendar.request_columns. The person's edit stands: never change it back. A changed Date needs nothing more (series order is handled below; Week follows in step 19). A changed What to post, Title / angle, Content type or Spec on a Draft ready or Approved row means the draft no longer fits: redraft it into a " v2" tab; Status stays as it is, and an Approved row gets a Notes line asking the approver to approve it again.- If a person typed an instruction into a cell instead of a value (for example "make this a reseller call-out" in Title / angle), copy it word for word to the end of Notes as the person's text, write a clean value with
"request": "<Row ID> <column>", and act on it as an ask. Whenrequests.enabledis false, list it under "Needs the approver" instead. milestone_changesstart the calendar builder's milestone change run (calendar-builder SKILL, Mode 3b) for that milestone, before drafting. Each milestone's Notes go to every linked row'sperson_items.json(make_bundles.py).- Run
python3 scripts/series_shift.py before.json shared/snapshot.json --as-of <date> --config shared/workspace.json. Write itswrites(they carry"series": true, so Draft ready rows can move), itsnew_rows(below the last row, as Proposed) and one Notes line per row fromnotes. Rows innotes_metawith"redraft": trueare redrafted into a v2 tab. Putbrief_linesunder "Changes made from your notes and ideas". - Write all of these first (lock check, guard, write), then read the Calendar again for step 7.
- Snapshot. Read each source per the adapter rules above and save it as named in
references/subagent-prompts.md: calendar (alsobefore.json), proof log, facts sheet, milestones, approver's notes, rules, plan rules, voice guide and the assets folder listing (all subfolders). Copy the client's chapter map (sources.long_form.chapter_map) aschapter-map.md. Read the long-form source (long_form.md) only once step 8 shows at least one row to draft, redraft or back up; a run with nothing to draft never reads it. Read eachsources.otherentry only when a step needs it (for example, published blog text for a teaser). - Select. Run
python3 scripts/select_rows.py before.json --config shared/workspace.json --facts shared/facts.json --milestones shared/milestones.json --as-of <date> --briefs <dates>(add--rerunfor a requested same-day full rerun, and--mode requests --updates <dates> --touched <Row IDs>in requests mode). Save the output asselect.jsonand use itsbrief_title.draftrows withgapsare drafted as base drafts.seriesrows ("post N of M" in Notes) are drafted only withinseries.draft_within_daysdays (default 14), unless their details have arrived: add a series row further out whenmatch_proof.pyreturns it asdraftwith an Advisor proof item, or when it has an open ask in Notes.refreshrows (a base draft whose gap is now filled, such as a teaser whose blog link arrived) are redrafted into a " v2" tab; Status stays Draft ready.fallback_row,in_review,proposed,team_ownedandblockedrows are not drafted. Cancelled and Backlog rows are not in the plan: no draft, no note, no swap, and no mention anywhere.
- Final text. For each
final_textrow, read its draft tab (Docsread_doc, the tab in Draft URL), save it, and runpython3 scripts/extract_final.py <doc>.json <tab URL>. Add aFinal textwrite with the output exactly. - The approver's edits (only when
edit_learning.enabledis true;references/edit-learning.md).- List the drafts folder and read only the docs changed since the previous brief was created, plus any doc a
final_textrow points to. A doc nobody touched has no new edits. - For each Draft ready, Fallback post, Approved or Posted row with a Draft URL in a read doc, run
extract_final.pyon its tab. Save{"<Row ID>": "<text above the dashed line>"}astab_texts.json, and each tab's two prompt lines asprompts.json({"<Row ID>": {"now": <image_prompt>, "original": <image_prompt_original>}}, from theextract_final.pyoutput). - Read the Approver edits tab (
calendar.tabs.approver_edits, A1:I) asshared/approver_edits.json. Runpython3 scripts/edit_capture.py before.json tab_texts.json shared/approver_edits.json --as-of <date> --prompts prompts.json --config shared/workspace.json. - For each row it returns, write "What changed" in one or two plain sentences from the difference (for a prompt row, what changed in the prompt). Append the rows to the Approver edits tab, read them back and save the tab again as
shared/approver_edits.json, so this run's drafters already learn from them. Never edit the draft tab or Original draft.
- List the drafts folder and read only the docs changed since the previous brief was created, plus any doc a
- Planner and proof match, in parallel. Start the planner agent with the plan bundle. Save its JSON return as
plan/return.json, and each drafter and checker return inreturns/, withsave_result.py report, before acting on it. In the main session, run proof intake'smatch_proof.pywith--config shared/workspace.json(proof-intake SKILL.md step 2) on the same snapshot and savematch.json. The planner'sprecedence_moves(only whenplan.precedenceisdated_events_first) become Date writes with"request": "plan precedence"and theirnoteas the Notes line, in step 19. - Draft, in parallel. Start one subagent per row in one message: a drafter for each
draft,refreshand v2 row, and a proof drafter for each proof slot the match plan says to draft. Each gets only its own bundle, built withmake_bundles.py ... draft --rows <ids> --select select.json(subagent-prompts.md): gap rows getgaps.json, every bundle getsperson_items.json, and, whenedit_learning.enabledis true, up toedit_learning.examples_per_draftof the approver's latest edited posts (same content type first) asapprover_edits.jsonand their prompt edits asapprover_prompt_edits.json. A content type with no playbook entry is drafted in the pattern of the nearest type; the drafter's "New type:" flag goes in the brief under "New post types to approve". Save each return inreturns/. - Swaps and fallback rows. Run
python3 scripts/swap_slots.py before.json select.json match.json --config shared/workspace.json --drafter-blocked <Row IDs whose drafter returned blocked> --backlog backlog.json --as-of <date>.- For each swap with
existing: false, write a one-line angle for its type and chapter (chapter-map.md; you may pick another chapter fromchapters_availablethat suits the type better, never one another swap uses) and save all swaps with angles asswaps_final.json. When the Backlog tab holds a post of a fitting type, offer it first ("or set<Row ID>to Planned on the Backlog tab"). - Start one drafter per swap with
fallback: true, in one message, on bundles frommake_bundles.py ... fallback --rows <ids> --swaps swaps_final.json. Rows infallbacks_skippedget the swap note only; list them in the brief. Each fallback becomes a row in step 19.
- For each swap with
- Visuals. Run
python3 scripts/visuals.py before.json shared/assets.txt returns/<every draft and fallback return, oldest first> --config shared/workspace.json. For eachresolveditem, write itsvisualand prunedvisual_alternativesback into that return before building its check bundle; whenchangedis true, say so in the tab's Flags for the approver. When an item hasswap_prompts: true, also swapimage_promptandimage_alt_promptin the return. Real drafts are resolved before fallbacks, so a fallback never takes a real draft's file. Anunresolveddraft is treated as a failed check with the fix "pick an unused visual or give an image prompt" (or "name the photo or graphic needed" when there is no AI image tool) and goes to the redraft in step 17. - Check, in parallel. For each returned draft, fallbacks included, build a check bundle and start a compliance-checker agent per draft, all in one message. Fallback check bundles use
--suffix -fb --content-type "<swap type>". The checker never sees the drafter's notes, flags, sources or reasoning. - Fresh snapshot before writing. If more than 60 minutes have passed since the step 7 read, or the run was paused or interrupted, read the Calendar again, save it as
before.json(keep the old one asbefore_old.json) and rerunselect_rows.py. Drop any draft whose row's Date, Status, Content type or Title / angle changed since the first read, and redo it from step 12 on the fresh row (a moved Date only changes the tab name and draft doc). Build every write in steps 19 and 21 from the freshbefore.json. - Redraft once. For each failed draft, start a fresh drafter subagent with the draft and the numbered fixes. After the redrafts return, run
visuals.pyagain on all final returns (each redraft after the draft it replaces) and apply its output as in step 14. Check each redraft with a fresh compliance-checker agent. Any draft whose visual changes on this pass, redrafted or not, is checked again with a fresh checker before step 18. A second failure stops there (hard rules). Fallbacks follow the same rule. - Write the drafts. In the main session, one row at a time: find or create the week's draft doc and add the post tab (draft-doc-format.md). A v2 redraft gets a new tab named as the original plus " v2". Put the checker result on the "Checker:" line. Add the image prompt lines when the return has them. Failed drafts are written too, with "Checker: failed twice" and the fixes under "Flags for the approver". Fallback tabs use the fallback tab name and the fallback reviewer line.
- Collect writes in
writes.json(format in thecalendar_writes.pyheader).<approver>iscompany.approver.name;<source label>isswaps.source_label;<prefix>isnotes.agent_prefix.- Passed drafts: Status
Draft ready(with"check": "pass"on that write), Draft URL, Original draft (only if empty), Visual, Sources (append), Notes (the drafter's flags), What's missing (the gaps: "To finish:" items first, then "Optional, to strengthen:"; clear what the draft resolved). Proof drafts also Proof item and the title placeholder, per proof-intake SKILL.md step 5. - v2 redrafts (refresh rows, rows a person changed, rows back from the Backlog, series rows with
redraft): Original draft with the new post and"v2": true, with the Draft URL of the v2 tab, so the Draft column and the edit capture compare with the post under review. - Failed drafts: Draft URL, Visual, Notes: "
<prefix>DD MMM YY: draft failed the compliance check twice (<short reasons>); not set to Draft ready.<approver>to review the tab." - Blocked rows (hard rule or hold only): What's missing (only if empty) and one Notes line, unless Notes already says it.
- Swaps, unless
existingis true: "<prefix>DD MMM YY: Proposed swap:<type>on<angle>(<source label>chN).<approver>to approve by changing the row." For teasers, start with theconditionaltext ("If the<blog>link is not in Notes on the blog row by the morning of DD MMM YY, proposed swap: ..."). Proof slots use the same type and chapter in proof intake's own line, one line in all, not two. - Fallback rows: one
insert_afterwrite per fallback: Date as the original, StatusFallback post, What to post, Pillar, Channel and Content type for the swap type, Title / angle "Backup for<Row ID>:<angle>", Visual, Notes "<prefix>DD MMM YY: Backup post for<Row ID>(checker:<result>). To use it, set this row to Approved and<Row ID>to Cancelled.", Sources, Row ID<Row ID>-FB, Week, AM/PM, Spec, Draft URL and Original draft. On the original row, one Notes line: "<prefix>DD MMM YY: Backup post added below (<Row ID>-FB). To use it, set it to Approved and this row to Cancelled." A fallback that failed twice is still added, with the failures in its Notes. - Person requests (steps 4 and 6): the row edits with
"request", plus the one Notes line ending "as asked in Notes" or "as asked on the Ideas tab". - Proposed rows due in the window: "
<prefix>DD MMM YY: due DD MMM YY and still Proposed, so not drafted.<approver>to set Planned or move it." unless already there, or unless the row is listed under a cancelled milestone in the planner'scancelled_milestones(the brief lists those once instead). - Planner
row_updates(What's missing only if the cell is empty),precedence_moves, and proof intake flags. - Final text writes.
- Week sync:
python3 scripts/calendar_writes.py weeksync before.json --config shared/workspace.jsonlists Week writes for rows whose Date a person moved by hand; add them. The Row ID never changes. - Stale conditions: for each row in
swap_slots.pystale_notes, the row's current line (step 20) uses today's dates, never the old one. A teaser condition takes the blog row's current date. A passed proof cut-off becomes "The sign-off cut-off of DD MMM YY has passed. If<item>is logged by<new date>, the next run drafts it; otherwise use the backup row." (proof-rules.md).
- Passed drafts: Status
- Tidy Notes. For every row with writes this run (and any row
notes_tidy.py --reportshows with 3 or more agent lines), write its one current line incurrent.json(carry forward anything still open; no history; keep "as asked" and "post N of M" wording). Read the Agent log tab (calendar.tabs.agent_log, A1:E) and save it asagent_log.json. Runpython3 scripts/notes_tidy.py before.json current.json --log agent_log.json --as-of <date> --config shared/workspace.json. Write itslog_rowsto itslog_rangeon the Agent log first, read them back, then put its writes ("tidy": true) inwrites.jsonin place of that row's Notes lines. Pass--log agent_log.jsontocalendar_writes.py planandguard, so a line already in the log is never appended again. - Lock check, Row ID guard and write.
- Run
python3 scripts/calendar_writes.py plan before.json writes.json --log agent_log.json --config shared/workspace.json. Report every refused write in the brief and never retry it. - Just before writing, read each of the plan's
guard_ranges(Status, and Notes to Row ID, of the target rows), save each exactly as returned (fresh_b.json,fresh_jm.json), and runpython3 scripts/calendar_writes.py guard before.json writes.json fresh_b.json fresh_jm.json --log agent_log.json --config shared/workspace.json > guard.json. - Write only guard's
acceptedcells, each to its exact range and value (Sheetsupdate_values, one call per row with null for cells in between; for another platform, the record in_source_row.json). Never write a cell fromrefused. Then add guard'sinsertsin the order given: Sheetsupdate_spreadsheetwith the item'sinsert_request(add the Calendar sheetId), thenupdate_valuesto itsrangewith itsvalues(null in the formula columns). - Guard's
refuseditems (Row ID or Status changed during the run) go at the top of the brief, with a line asking the approver to set Cancelled or Backlog instead of deleting rows.plan_refusedrepeats the column-lock refusals already reported. - Write the proof log's "Used in calendar row" column as proof intake says, then read it back.
- Run
- Verify. Read the Calendar again, save it as
after.json, and runpython3 scripts/calendar_writes.py verify before.json after.json writes.json --guard guard.json --config shared/workspace.json. Cells a person changed during the run are listed underchanged_by_person_during_run(Agent log RUN row).deleted_row_ids,added_row_idsand themoved_rowscount go at the top of the brief. Any unexpected change goes at the top of the brief and in the output to the person. - Voice guide proposals (monthly run only, and only when
edit_learning.enabledis true). Start one subagent with the Approver edits rows captured since the last proposals (references/edit-learning.md). It returns up toedit_learning.monthly_proposals_maxproposed voice guide changes, each backed by at leastedit_learning.min_supporting_editsedits. Put them in the brief under "Voice guide changes to approve". Never edit the voice guide: the approver makes the change. - Sort, then hide Cancelled rows. Run
python3 scripts/backlog.py sort after.json --sheet <Calendar sheetId> --config shared/workspace.jsonand send its request (Date, then AM/PM, then Row ID, so a -FB row sits under its original; the header and formula rows stay in place). Read the Calendar again asafter.json. Hidden rows do not move with their data, so hide straight after sorting: read the Calendar's hidden rows (get_spreadsheet,rowMetadata.hiddenByUser), runpython3 scripts/backlog.py hide after.json --sheet <Calendar sheetId> --hidden <hidden row numbers> --config shared/workspace.jsonand send itsrequestsin oneupdate_spreadsheetcall. Every Cancelled row is hidden; a hidden row a person set back from Cancelled is shown again. No cell value changes. - Agent log RUN row. Append one row to the Agent log: Date moved = run date, Row ID
RUN, AgentOrchestrator, Text = the full run details (Notes tidied, refused writes, tab guard skips, final text copied, edits captured, cells changed by a person during the run, config warnings), ReasonRun details. - Save the Agent snapshot. Run
python3 scripts/snapshot.py save after.json shared/milestones.json shared/snapshot.json --config shared/workspace.jsonand write itsvaluesto itsrangeon the hidden Agent snapshot tab with oneupdate_valuescall. This is always the last calendar write of a run, so the next run sees only what people changed since. - Brief. Write
Weekly brief - DD MMM YYin the briefs folder of the mode in use (brief-format.md). Keep it short: Risks lists only what is not already under another heading, repeated risks are dropped, and Run details is three lines.
Requests mode: requests
A light run for weekday asks, so a request made the day before a post still gets a draft. Argument requests (/weekly-run <test|live> requests); also used for a second run on a day that already has a brief, unless the person asked for rerun. A full run happens once a week (run.full_run); requests runs follow run.requests_runs. When requests.enabled is false, this mode still handles Backlog moves, series shifts and redrafts of rows a person changed, but lists asks in the update instead of acting on them.
- Steps 1, 4 (asks, new ideas through the calendar builder's ideas run, filing), 5 (Backlog moves) and 6 (hand edits, milestone changes and series shifts since the last snapshot).
- Then only the rows an open ask, a hand edit, a Backlog move, a series shift or a new idea touches, dated within
series.draft_within_daysdays (or in a series whose details arrived): steps 7 and 8 for those rows, then 12 and 14 to 22 as normal. - No planner, no fallback rows, no edit capture, no monthly work. News in an ask is filed as in step 4 (proof log only when
proof.approver_word_is_signoffis true). The proof match runs only when a proof slot or advisory row is touched. - Always end with steps 24 and 26 (sort, hide, snapshot), even when nothing else was done.
- If something was done, write
Update - DD MMM YYin the briefs folder (brief-format.md, "Update") and end with the short output. If nothing was open, write no update and end with one line: "Nothing open."
Missed runs
If missed_runs is not empty, a scheduled full run did not happen (computer off, app closed, connector signed out). This run recovers it: the selection covers overdue and due rows. Name each missed full run day in the brief. Overdue rows are listed, never drafted. Missed requests runs need no recovery: the next run's snapshot compare picks up every change.
Scheduled runs
Two scheduled tasks, on the client's own account, from references/scheduled-prompts.md: the full run on run.full_run.weekday at run.full_run.time, and the requests run on run.requests_runs.weekdays at run.requests_runs.time, both in company.timezone. Scheduled runs are unattended: never stop to ask; take the safe option when a choice is truly unclear.
Dates
Every date an agent writes (Notes, brief, doc names, tab names, review rows, output to the person) is DD MMM YY with a leading zero and an uppercase month: 03 NOV 26, never 3 NOV 26. Tab names add the weekday: TUE 03 NOV 26. The scripts print dates this way and read typed dates per dates.typed_order.
Output to the person
Up to eight lines: rows set to Draft ready (Row ID, date, tab link), backup rows (Row ID, swap type, tab link), changes made from notes, ideas and hand edits, AI images to make, rows blocked or failed with the reason, Approved rows due within 2 days with something still missing, refused or guarded writes, missed runs, and the brief or update link. Never mention Cancelled rows. Dates DD MMM YY with an uppercase month, spelling per writing.spelling, no em dashes.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

