#+TITLE: Broadcast Workflow #+AUTHOR: Craig Jennings & Claude #+DATE: 2026-05-29 * Overview Fan out a single message to every AI project's inbox in one operation. Discovers projects by fingerprint (any directory with =.ai/protocols.org=) and delivers via the existing =inbox-send.py= per-target. Say a thing once instead of hand-walking 20+ projects. Two modes share the same fan-out plumbing and differ only in what the message carries and what the receiver does with it: - *Announcement* — a tooling, capability, or rule change every project's agent should know about. Maintainer-facing. - *Situational* — a life or work event in Craig's world (travel, a visitor, a birth or death, quitting a job, a move) that should reach every project's agent so none is missing context. Craig says it once; each agent folds it into its own work on its own judgment. The mode picks the message template (Compose) and the receiving contract. Everything else — discovery, scope confirmation, fan-out, reporting — is shared. * When to Use This Workflow *Announcement triggers:* - "broadcast this to every project" - "notify every project about " - "fan out this announcement" - "let every project know X is available" Automatic announcement triggers: - *New machine-global capability landed.* A new script in =~/.local/bin/=, a new MCP server, a new tool (e.g. =signal-cli=). Projects need to know it's available. - *Shared rule or protocol change* in =claude-rules/= or =claude-templates/.ai/= that materially changes how every project's agent should behave. - *Deprecation notice* — a script, workflow, or rule going away; give every project a chance to migrate. *Situational triggers:* - "broadcast the to all projects" (e.g. "broadcast the travel to all projects") - "broadcast that " - "let every project know I'll be ..." Situational mode is user-driven only — never automatic. Craig decides an event is worth every project knowing. * When NOT to Use This Workflow - *Project-specific work.* A handoff for one project goes through =inbox-send= directly, not broadcast. - *Routine status updates.* The session log and todo.org cover routine work. - *Bulk noise.* Every broadcast adds N inbox files across the fleet. Use sparingly. Ask whether projects actually need to know. - *Every commit.* Most commits are project-internal hygiene the other projects don't care about. Cross-project workflow updates already rsync into every project's =.ai/= at next startup, so the capability lands without a broadcast. Broadcast only when projects need to *act* on the change or *hold* the context. The startup rsync carries the bits; the broadcast carries the *attention*, and attention is the costly resource. * Cadence Guideline Broadcasts are event-level, not commit-level. A reasonable cadence is one to four per month. Each broadcast costs N inbox processings across the fleet (currently ~23 targets), so daily broadcasts mean daily noise and train projects to ignore the inbox — then a real handoff gets missed. If a session ships several broadcastable changes, bundle them into one broadcast at session end rather than firing one per commit. Situational broadcasts are naturally rare (a real life or work event), so the same restraint applies without much effort — but don't withhold one that genuinely bears on how the agents should act. * The Fan-Out (shared by both modes) ** Phase A — Discover targets #+begin_src bash python3 .ai/scripts/broadcast.py --list #+end_src The helper scans =~/code/=, =~/projects/=, =~/.emacs.d= for any directory containing =.ai/protocols.org=. Prints basename and full path of each, sender-excluded (the current project never receives its own broadcast). ** Phase B — Compose the message Pick the mode, write the body to =/tmp/broadcast-.org= using that mode's template (below), then continue to Phase C. The mode templates are the only place the two modes diverge. ** Phase C — Confirm scope with Craig Surface the discovered project list and the message inline. Both modes confirm before fan-out — it reaches many inboxes and a situational message goes out under Craig's situation, so the content gets one approval first. #+begin_example Broadcast scope (): - Target projects: (list) - Message: <2-line summary> - : 1. Send to all targets (recommended) 2. Exclude specific projects (name them) 3. Cancel — message stays at /tmp/broadcast-.org #+end_example ** Phase D — Fan out #+begin_src bash python3 .ai/scripts/broadcast.py \ --file /tmp/broadcast-.org \ [--exclude project1 --exclude project2 ...] #+end_src The helper iterates targets, runs =inbox-send.py --file = per target, and captures success/failure per project. The =from-= prefix in each resulting filename traces provenance. ** Phase E — Report Summarize the fan-out: total targets discovered, sent successfully (count), failed (list with reason), excluded (list with reason). Surface any failures — a partial broadcast is the failure mode you'll never notice otherwise. ** Phase F — Cleanup Delete =/tmp/broadcast-.org=. The content lives in each target's inbox now. * Mode A — Announcement A tooling, capability, or rule change. Write the body to =/tmp/broadcast-.org= with this structure — rigid on purpose, so every project's next session scans 20+ broadcasts in seconds: #+begin_example ,#+TITLE: ,#+DATE: YYYY-MM-DD ,#+SOURCE: ,* What's new ,* How to use it ,* Why this matters / when to use ,* Action required - =FYI=, no action required (most broadcasts) - =Update workflow X= to reference the new capability - =Deprecate workflow Y= by date Z #+end_example * Mode B — Situational A life or work event Craig wants every project to know. The point is to beat the silo problem: Craig forgets to mention something during a conversation with one project, but it actually bears on how that project works and on the agent's decisions or ideas. Broadcast once, and every agent already knows by the time he's chatting with them. ** Composing principle - *No per-project tailoring.* The broadcasting agent does NOT figure out how the event is relevant to each project. One general message goes to all. Each receiving agent decides relevance to its own current tasks. - *General, not comprehensive.* Communicate a good amount of detail, but don't try to be exhaustive for a complex situation. Enough that an agent unfamiliar with it gets the picture and knows the shape of what changed. The receiving agent can ask Craig follow-up questions after startup. - *Gather, then ask for gaps.* Pull the salient facts from the current session and project. Ask Craig only for the few you don't have (the concrete ones an agent would need — dates, who, where, what's changing). Write the body to =/tmp/broadcast-.org= with this structure: #+begin_example ,#+TITLE: Broadcast: ,#+DATE: YYYY-MM-DD ,#+SOURCE: ,* What's happening ,* For the receiving agent This is situational context Craig wanted every project to have — said once and broadcast, not a task and not tailored to this project. - Decide for yourself whether and how it bears on this project's work and your suggestions. He is not prescribing actions; you judge relevance. - Record it so it persists: add it to this project's =notes.org= Active Reminders. Time-box it with an end date if the situation has one (a return date, a departure date); otherwise hold it as standing context until it is no longer relevant. - Surface it at the next startup and raise it if it touches current work. - Ask Craig any follow-up questions then — this message is deliberately general. #+end_example The "For the receiving agent" block is fixed text — it travels with every situational broadcast so the message is self-describing. A receiving project's =process-inbox= reads it and acts on those instructions without needing any special-casing; the value gate accepts it as situational awareness that improves how the project works. ** Receiving behavior (what a project does with an incoming situational broadcast) When =process-inbox= encounters a =Broadcast:= item, the disposition is *record-and-hold*, not file-as-task: 1. Add a dated entry to =notes.org= Active Reminders capturing the situation and its end date (if any). 2. If the event bears on an open task, note the connection in that task's body. 3. Surface it at the next startup; retire it once its end date passes or it stops being relevant. * Common Mistakes 1. *Broadcasting project-specific work.* Each broadcast costs N inbox files across the fleet. Routine handoffs go through =inbox-send= directly. 2. *Per-project tailoring in situational mode.* The broadcasting agent does not analyze relevance per project — that's the receiving agent's job. One general message, full stop. 3. *Over-engineering the situational summary.* General, not comprehensive. The receiver asks follow-ups; don't stall trying to anticipate every project's needs. 4. *Skipping the announcement structure.* Free-form announcements force every recipient to parse them differently. Use the rigid headings. 5. *Sender-includes-itself.* The discovery helper excludes the sender automatically. Don't override it — broadcasting to your own inbox creates a self-reply loop. 6. *Forgetting Phase E.* A broadcast that partially succeeded is the failure mode you'll never notice. Always check the per-target results. 7. *Announcement without an "Action required" line, or situational without the "For the receiving agent" block.* Each mode's recipients need to know what the message is for. * Living Document If the discovery roots change (a new top-level directory for AI projects), update =broadcast.py='s =SEARCH_ROOTS=. If the per-broadcast structure proves too rigid or too loose, tune the mode templates. If recipient projects complain about broadcast noise, the rule is "broadcast less," not "structure broadcasts harder." If situational broadcasts start needing per-project routing, that's a sign the silo is better solved by the shared org-roam KB than by fan-out — revisit before adding routing logic here.