agentleFS
lauradfarbolauradfarbo/social-draft-desk/skills/weekly-orchestrator/SKILL.md

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.

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
  1. Very specific:

    Inputs: every source in workspace.json (references/locations.md lists which are read and written in each mode).

    line 14
  2. Distinctive:

    Access: read-only on every reference source.

    line 16
  3. Distinctive:

    Never publish, post, send email or submit anything.

    line 20
  4. Very specific:

    Read workspace.json first.

    line 21
  5. Distinctive:

    One writer: only the main session writes to the calendar, its tabs, the draft docs, the proof log and the brief.

    line 28
  6. Very specific:

    Mode: use the TEST locations unless the person (or the scheduled task prompt) says live, shadow or test shadow.

    line 40
  7. Very specific:

    If any doc lacks its expect_phrase, treat it as cut off and stop the run.

    line 58
  8. Very specific:

    Read it, save it as weekly/<run date>/shared/workspace.json, and run python3 skills/workspace-setup/scripts/check_workspace.py …

    line 66
  9. Very specific:

    Backlog moves: Read the Calendar (A1:V) and the Backlog tab (calendar.tabs.backlog, A1:X), save both, and run python3 scripts/backlog.py …

    line 73
  10. Very specific:

    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 …

    line 74
  11. Very specific:

    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 …

    line 75
  12. Very specific:

    Hand edits, milestone changes and series: Read the Milestones tab (A1:G) as shared/milestones.json and the hidden Agent snapshot tab …

    line 78
  13. Distinctive:

    Write all of these first (lock check, guard, write), then read the Calendar again for step 7.

    line 83
  14. Very specific:

    Select: Run python3 scripts/select_rows.py before.json --config shared/workspace.json --facts shared/facts.json --milestones …

    line 85
  15. Very specific:

    Read the Approver edits tab (calendar.tabs.approver_edits, A1:I) as shared/approver_edits.json.

    line 94
  16. Very specific:

    Swaps and fallback rows: Run python3 scripts/swap_slots.py before.json select.json match.json --config shared/workspace.json …

    line 98
  17. Very specific:

    Start one drafter per swap with fallback: true, in one message, on bundles from make_bundles.py ...

    line 100
  18. Very specific:

    Run python3 scripts/calendar_writes.py plan before.json writes.json --log agent_log.json --config shared/workspace.json.

    line 121
  19. Distinctive:

    Write the proof log's "Used in calendar row" column as proof intake says, then read it back.

    line 125
  20. Very specific:

    Save the Agent snapshot: Run python3 scripts/snapshot.py save after.json shared/milestones.json shared/snapshot.json --config …

    line 130
  21. Very specific:

    Brief: Write Weekly brief - DD MMM YY in the briefs folder of the mode in use (brief-format.md).

    line 131
  22. Very specific:

    No planner, no fallback rows, no edit capture, no monthly work.

    line 139
  23. Distinctive:

    Always end with steps 24 and 26 (sort, hide, snapshot), even when nothing else was done.

    line 140
  24. Very specific:

    If something was done, write Update - DD MMM YY in the briefs folder (brief-format.md, "Update") and end with the short output.

    line 141

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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.json exists and passes check_workspace.py; the calendar follows calendar-builder/references/calendar-schema.md, including the Backlog, Agent log, approver's notes, Approver edits and hidden Agent snapshot tabs (names in calendar.tabs); the agent register records a passing TEST run before any live run.
  • Inputs: every source in workspace.json (references/locations.md lists 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_signoff is 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.json first. 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.enabled is true, the default). What a person in requests.from writes 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.md explains 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 per dates.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 prefix Agents). 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 are W{iso week:02d}-{seq:02d}, never reused across years (a new row takes the next number after the highest used). Alternating cadences are anchored on calendar.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 plan first, then calendar_writes.py guard on a fresh read just before writing (the Row ID and Status of each target row must still match), and every run ends with calendar_writes.py verify on 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's tab_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) and Fallback post (only on a backup row the run adds); the list is calendar.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 Backlog on 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 to Planned there 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_weeks either side, including twice in one run (visuals.py). When visuals.ai_image_tool is 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. When visuals.ai_image_tool is 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_days days 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, Status Fallback post, the full row filled (a type from swaps.types, angle and long-form chapter from swap_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 a plan.dependencies item 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.precedence is dated_events_first, the default): a dated event keeps its slot over an evergreen row; plan_check.py returns the moves.
  • Mode: use the TEST locations unless the person (or the scheduled task prompt) says live, shadow or test shadow. If it is not stated, ask. Use live only when the agent register records a passing TEST run with the current config.
  • Shadow mode (shadow or test shadow): read references/shadow-mode.md first 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_values response 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 one update_values call, 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 with get_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, then python3 scripts/doc_text.py <saved>.json <name>.md --expect "<expect_phrase>". Never Drive read_file_content for 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's expect_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.

  1. Load the config.
    • Take workspace.json from --config. If none is given, ask the person where it is. If none exists, run the workspace-setup skill first and start this run again afterward.
    • Read it, save it as weekly/<run date>/shared/workspace.json, and run python3 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-of in 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).
  2. 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 YY for the run date already exists, switch to requests mode (below) and write Update - DD MMM YY, unless the person asked for rerun: then do the full run and pass --rerun to select_rows.py (its brief_title adds " (2)"). Missed full runs are reported by select_rows.py (step 8).
  3. 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.weekday runs the ideas run and any milestone change run only. Collect the builder's summary of proposed rows and any brief_lines from skipped tab writes for the brief.
  4. Requests (when requests.enabled is false, run the scan but act on nothing: list unhandled asks under "Needs the approver"). Read the Calendar (A1:V) as before.json, the Ideas tab (calendar.tabs.ideas, A1:H) and the approver's notes tab (calendar.tabs.approver_notes, A1:G, saved as shared/approver_notes.json). Run python3 scripts/request_scan.py before.json --ideas ideas.json --as-of <date> --config shared/workspace.json. For every ask with kind: ask and handled: false, decide the row changes (references/requests.md) and put them in writes.json with "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_signoff is true, log it in the proof log with proof intake's proof_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.py and read back, then saved again as shared/approver_notes.json. Never log the same fact twice (same source and same words).
  5. Backlog moves. Read the Calendar (A1:V) and the Backlog tab (calendar.tabs.backlog, A1:X), save both, and run python3 scripts/backlog.py plan before.json backlog.json --as-of <date> --config shared/workspace.json. If out and in are both empty, go on. Otherwise:
    • 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 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 its requests in one update_spreadsheet call. A row is removed only when its copy exists on the other tab.
    • Report refused at the top of the brief. Put brief_lines under "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.
  6. Hand edits, milestone changes and series. Read the Milestones tab (A1:G) as shared/milestones.json and the hidden Agent snapshot tab (calendar.tabs.agent_snapshot, A1:L) as shared/snapshot.json (empty on the first run: nothing is reported). Run python3 scripts/snapshot.py compare before.json shared/milestones.json shared/snapshot.json --config shared/workspace.json.
    • row_changes are asks a person made by editing a cell in calendar.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. When requests.enabled is false, list it under "Needs the approver" instead.
    • milestone_changes start 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's person_items.json (make_bundles.py).
    • Run python3 scripts/series_shift.py before.json shared/snapshot.json --as-of <date> --config shared/workspace.json. Write its writes (they carry "series": true, so Draft ready rows can move), its new_rows (below the last row, as Proposed) and one Notes line per row from notes. Rows in notes_meta with "redraft": true are redrafted into a v2 tab. Put brief_lines under "Changes made from your notes and ideas".
    • Write all of these first (lock check, guard, write), then read the Calendar again for step 7.
  7. Snapshot. Read each source per the adapter rules above and save it as named in references/subagent-prompts.md: calendar (also before.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) as chapter-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 each sources.other entry only when a step needs it (for example, published blog text for a teaser).
  8. 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 --rerun for a requested same-day full rerun, and --mode requests --updates <dates> --touched <Row IDs> in requests mode). Save the output as select.json and use its brief_title.
    • draft rows with gaps are drafted as base drafts.
    • series rows ("post N of M" in Notes) are drafted only within series.draft_within_days days (default 14), unless their details have arrived: add a series row further out when match_proof.py returns it as draft with an Advisor proof item, or when it has an open ask in Notes.
    • refresh rows (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_owned and blocked rows are not drafted. Cancelled and Backlog rows are not in the plan: no draft, no note, no swap, and no mention anywhere.
  9. Final text. For each final_text row, read its draft tab (Docs read_doc, the tab in Draft URL), save it, and run python3 scripts/extract_final.py <doc>.json <tab URL>. Add a Final text write with the output exactly.
  10. The approver's edits (only when edit_learning.enabled is 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_text row 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.py on its tab. Save {"<Row ID>": "<text above the dashed line>"} as tab_texts.json, and each tab's two prompt lines as prompts.json ({"<Row ID>": {"now": <image_prompt>, "original": <image_prompt_original>}}, from the extract_final.py output).
    • Read the Approver edits tab (calendar.tabs.approver_edits, A1:I) as shared/approver_edits.json. Run python3 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.
  11. 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 in returns/, with save_result.py report, before acting on it. In the main session, run proof intake's match_proof.py with --config shared/workspace.json (proof-intake SKILL.md step 2) on the same snapshot and save match.json. The planner's precedence_moves (only when plan.precedence is dated_events_first) become Date writes with "request": "plan precedence" and their note as the Notes line, in step 19.
  12. Draft, in parallel. Start one subagent per row in one message: a drafter for each draft, refresh and v2 row, and a proof drafter for each proof slot the match plan says to draft. Each gets only its own bundle, built with make_bundles.py ... draft --rows <ids> --select select.json (subagent-prompts.md): gap rows get gaps.json, every bundle gets person_items.json, and, when edit_learning.enabled is true, up to edit_learning.examples_per_draft of the approver's latest edited posts (same content type first) as approver_edits.json and their prompt edits as approver_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 in returns/.
  13. 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 from chapters_available that suits the type better, never one another swap uses) and save all swaps with angles as swaps_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 from make_bundles.py ... fallback --rows <ids> --swaps swaps_final.json. Rows in fallbacks_skipped get the swap note only; list them in the brief. Each fallback becomes a row in step 19.
  14. Visuals. Run python3 scripts/visuals.py before.json shared/assets.txt returns/<every draft and fallback return, oldest first> --config shared/workspace.json. For each resolved item, write its visual and pruned visual_alternatives back into that return before building its check bundle; when changed is true, say so in the tab's Flags for the approver. When an item has swap_prompts: true, also swap image_prompt and image_alt_prompt in the return. Real drafts are resolved before fallbacks, so a fallback never takes a real draft's file. An unresolved draft 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.
  15. 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.
  16. 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 as before_old.json) and rerun select_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 fresh before.json.
  17. Redraft once. For each failed draft, start a fresh drafter subagent with the draft and the numbered fixes. After the redrafts return, run visuals.py again 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.
  18. 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.
  19. Collect writes in writes.json (format in the calendar_writes.py header). <approver> is company.approver.name; <source label> is swaps.source_label; <prefix> is notes.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 existing is true: "<prefix> DD MMM YY: Proposed swap: <type> on <angle> (<source label> chN). <approver> to approve by changing the row." For teasers, start with the conditional text ("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_after write per fallback: Date as the original, Status Fallback 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's cancelled_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.json lists 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.py stale_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).
  20. Tidy Notes. For every row with writes this run (and any row notes_tidy.py --report shows with 3 or more agent lines), write its one current line in current.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 as agent_log.json. Run python3 scripts/notes_tidy.py before.json current.json --log agent_log.json --as-of <date> --config shared/workspace.json. Write its log_rows to its log_range on the Agent log first, read them back, then put its writes ("tidy": true) in writes.json in place of that row's Notes lines. Pass --log agent_log.json to calendar_writes.py plan and guard, so a line already in the log is never appended again.
  21. 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 run python3 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 accepted cells, each to its exact range and value (Sheets update_values, one call per row with null for cells in between; for another platform, the record in _source_row.json). Never write a cell from refused. Then add guard's inserts in the order given: Sheets update_spreadsheet with the item's insert_request (add the Calendar sheetId), then update_values to its range with its values (null in the formula columns).
    • Guard's refused items (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_refused repeats the column-lock refusals already reported.
    • Write the proof log's "Used in calendar row" column as proof intake says, then read it back.
  22. Verify. Read the Calendar again, save it as after.json, and run python3 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 under changed_by_person_during_run (Agent log RUN row). deleted_row_ids, added_row_ids and the moved_rows count go at the top of the brief. Any unexpected change goes at the top of the brief and in the output to the person.
  23. Voice guide proposals (monthly run only, and only when edit_learning.enabled is true). Start one subagent with the Approver edits rows captured since the last proposals (references/edit-learning.md). It returns up to edit_learning.monthly_proposals_max proposed voice guide changes, each backed by at least edit_learning.min_supporting_edits edits. Put them in the brief under "Voice guide changes to approve". Never edit the voice guide: the approver makes the change.
  24. Sort, then hide Cancelled rows. Run python3 scripts/backlog.py sort after.json --sheet <Calendar sheetId> --config shared/workspace.json and 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 as after.json. Hidden rows do not move with their data, so hide straight after sorting: read the Calendar's hidden rows (get_spreadsheet, rowMetadata.hiddenByUser), run python3 scripts/backlog.py hide after.json --sheet <Calendar sheetId> --hidden <hidden row numbers> --config shared/workspace.json and send its requests in one update_spreadsheet call. Every Cancelled row is hidden; a hidden row a person set back from Cancelled is shown again. No cell value changes.
  25. Agent log RUN row. Append one row to the Agent log: Date moved = run date, Row ID RUN, Agent Orchestrator, 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), Reason Run details.
  26. Save the Agent snapshot. Run python3 scripts/snapshot.py save after.json shared/milestones.json shared/snapshot.json --config shared/workspace.json and write its values to its range on the hidden Agent snapshot tab with one update_values call. This is always the last calendar write of a run, so the next run sees only what people changed since.
  27. Brief. Write Weekly brief - DD MMM YY in 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_days days (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_signoff is 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 YY in 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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.