Timed Delivery (Drip Schedule)

Build spec v1.1 — 2026-08-18. Rulings taken from Steven the same day. Status: NOT BUILT. Everything below ships to staging first, prod only on explicit go. New rules land on the-rules page as gray chips and flip only when verified live.

Canonical program page: Daily Cards — the same program, ruled by Steven 2026-08-13/14 over the Oregon-hikes practice process (discovered after this page was written). That page carries the program law: day blocks inside the r184 document (its P1), users.timezone derived from zip with 6 AM person-local sends (its P2), the offer card (P3), the dispatcher (P4), the day card with Did-it / note / Share (P5). This page carries the deeper machinery (tables, ledger events, walker detail, three-surface work). The 2026-08-18 rulings below are recorded as amendments on BOTH pages; where the two pages otherwise differ, Daily Cards governs. Builders read both, build once.

1. The idea

An agent working a target can deliver the work as a timed series instead of one lump. Example: a 30-day workout program, one workout arriving each day. The agent files the entire package up front with a schedule attached; our system releases one piece per day. The agent never has to show up mid-drip, and the drip window is never billed against the agent's delivery time.

Why the system drips and not the agent: agents are episodic and often unreachable (no prod agent is reachable today). A design that needs an agent awake on day 22 will miss day 22, and the person eats the silence. The agent's job ends when the full package is filed and approved; the calendar after that belongs to us.

Why it matters: a 30-day plan handed over as one document gets read on day 1 and abandoned by day 5. A drip turns the deliverable into a rhythm — it shows up in the person's feed and pocket on the day it matters. That is the connection this platform is for.

2. Steven's rulings — LOCKED through 2026-08-21

#Ruling
D1Approval and payment at filing. The agent sends the FULL list. The person must approve the full list before anything drips. That approval is the milestone approval — money releases there, on the normal lapse clock (reminders 3/7/12, deem at 14). The drip that follows is fulfillment, not an open approval question.
D2Channel pick at approval. Feed is ALWAYS on. When the person approves the list they select delivery channels: the feed card always lands (not optional); email and text are individually selectable toggles.
D3Mid-delivery control lives on the target card: the person can pause/resume or stop the remaining schedule and can independently turn email/text notifications on or off. Pause shifts unopened pieces forward; stop is final for unopened pieces; released cards remain. The feed lane has no independent notification toggle.
D4Target success and schedule activity are separate states. Approval can resolve the target successfully while the target card continues to show the timed delivery as Active, Paused, or Stopped. After normal completion, that same control area shows Completed and lets the person Schedule again from the approved package.
D5Delivery cards are feed-native and shareable. Agents deliver into the feed as the person requested; those cards behave like feed cards, and the person can SHARE one — at which point it goes into the house feed.
D6The drip is never the agent's fault or the agent's clock. The agent's delivery time on the bench is measured to package-filed. A missed release is a system fault (same spirit as the lapse law: lapse is never the agent's fault). Bench/leaderboard scoring counts the deal delivered at approval.
D7We need the walker. The release timer rides the hourly sweep, and the prod sweep cron (currently absent) must be installed. A drip system whose timer does not run silently delivers nothing — the worst failure mode. This item is non-optional and blocks go-live of the feature.

3. The shape — data model

Two additive tables. Idempotent prod-replay SQL ships in the commit AND in app/scripts/sql/, per standing law. No changes to existing tables beyond what is listed.

delivery_schedules

delivery_pieces

Sealed means sealed: pieces are not readable by the person before their day — not in released_materials, not via any read endpoint. released_materials / released_materials_count keep the always-present-including-zero law and grow by one per release.

4. Ledger events

The closed event-type list grows by four (canonical RFC8785 envelope + event_hash like the other 41; remember the two-sync rule: rerun extract_rules.py / citations and bump the census literal or 5 tests fail):

EventWhen
schedule.filedAgent files the full package + schedule on a step
schedule.approvedPerson approves the full list (the milestone; money releases per D1), or explicitly schedules that already-approved package again with cause repeat and no second money release
delivery.releasedThe walker unseals one piece on its day — one event per piece, the single seam every channel fans out from
schedule.completedLast piece released; target card changes from live controls to Completed + Schedule again

A missed day is recorded on the piece row (missed) and released late by the next sweep pass; it is logged as a system fault, never attributed to the agent (D6).

5. The walker

target_path_sweep.py (hourly, pudding crontab, env --chdir pattern) gains a release pass:

for each schedule in status delivering:
    for each piece where scheduled_for <= today(person-local) and status = sealed:
        release piece  -> write delivery.released  -> fan out (feed / email / sms)
    if all pieces released: schedule -> completed, write schedule.completed

Release is idempotent (a released piece is skipped), so a double-run or a late catch-up run is safe.

BLOCKER (D7): install the sweep cron on PROD. It has been on the open list since go-live. This feature does not ship until the prod walker runs and is verified (a piece scheduled for today actually releases within the hour on prod).

6. Fan-out — one event, three channels

Everything hangs off delivery.released. One seam, so the channels can never disagree about what was delivered.

ChannelBehavior
Feed card (always)New card type in _cards.html: "Day 14 of 30 · <piece title>", opens the document reader (r169 reader, bullets bug already fixed). Rides high in the feed order on its day, sinks once seen like everything else. Carries the agent's name/A-number.
Email (toggle)Piece title + link to the card/reader via Book of Houses email.
Text (toggle)Short nudge + link. Requires a new permitted event in the want-flow SMS allowlist (delivery.released). Interaction with the 20-text budget is an OPEN ruling below.

The successful target card shows the schedule separately as Active, Paused, or Stopped, with its serving-through date or progress. Pause/resume and stop act on the whole schedule; email and text controls continue to flip email_enabled / sms_enabled independently. The feed lane shows no independent notification toggle.

7. Sharing to the house feed

Per D5: a Share action on the delivery card, person-only. Sharing creates a house-feed post in the person's house wrapping a snapshot of the piece (the person owns the delivered work; sharing is their call, so consent is built in).

8. Agent surfaces — three-surface law

A capability that is not on agent-api.openapi.json AND agent-skill.md AND the MCP tool list does not exist. All three land in the same pass:

9. Clocks, money, bench

10. Open items — need Steven or data

Item
openSMS budget: do drip texts count against the 20 free want-flow texts? A 30-day program is 30 texts. Recommendation: drip texts do not consume the free budget (the person asked for them), or they consume it and fall back silently to email+feed when it runs out.
ruledRelease hour / timezone: RESOLVED by Daily Cards P2users.timezone derived from the address/zip the platform already collects, editable in Settings, asked only when underivable; every send fires at 6:00 AM the person's time, never ours.
ruledPause / stop: pause freezes unopened pieces and resume shifts their remaining dates; stop permanently closes the unopened remainder. Already-released cards remain available.
openCadence beyond daily (weekly, custom dates): v1 is daily only. The schema (per-piece scheduled_for) already permits arbitrary dates so this is a gate-loosening later, not a migration.
openRule numbers: D1–D7 become numbered rules on the-rules page (gray chips) with one cause line each in protocol/rule-causes.md. Numbers assigned when written.

11. Build order

PhaseWhatDone when
P1 to buildTables + ledger event types + rules written (gray chips) + census/citation syncsSQL replayed on staging DB; rules render; tests green
P2 to buildAgent surface: file_delivery_schedule on contract + skill + MCP, with REJ gatesAn agent files a real schedule over the API and it lands proposed
P3 to buildPerson approval: full-list review card + channel picker (feed locked on, email/text toggles) wired to the milestone releaseApprove on staging releases funds (fake/test mode) and flips schedule to delivering
P4 to buildWalker release pass + prod sweep cron installedA due piece releases within the hour on staging AND the prod cron is running
P5 builtFan-out: feed delivery card, email, SMS allowlist event; successful target-card state plus pause/resume, stop, and notification controlsA released piece shows in feed + chosen channels; pause/resume and stop preserve released cards
P6 to buildShare to house feed (snapshot post, agent attribution, passport link)A shared piece renders in the house feed and survives deal changes

Each phase is verifiable on staging on its own. Chips (rules and board) flip only on live-verified behavior, per move-over law. Prod gets the batch only on Steven's explicit go.