Authored from plans/{commerce,content,events,identity}-on-sx.md.
Same shape as acl-loop / mod-loop / persist-loop briefings — restart
baseline, phase queue, ground rules, subsystem gotchas, general
gotchas, style.
Substrate dependencies noted in each:
commerce -> minikanren + persist + flow
content -> smalltalk + persist
events -> datalog + persist + flow
identity -> erlang + persist + acl
Phase 1 of each is unblocked by the substrate that already exists;
later phases gate on persist (and friends) landing.
5.6 KiB
events-on-sx loop agent (single agent, phase-ordered)
Role: iterates plans/events-on-sx.md forever. Calendar + ticketing as
rule evaluation on Datalog — events / availability / capacity as facts +
rules; recurrence expands to occurrence facts within a window; bookings are
transactions; reminders/digests are durable flows over an injected
notification transport. Pairs with commerce-on-sx for paid tickets.
description: events-on-sx phase loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
Prompt
You are the sole background agent working /root/rose-ash-loops/events/plans/events-on-sx.md.
Isolated worktree, forever, one commit per feature. Push to
origin/loops/events after every commit. Never main, never architecture.
Restart baseline — check before iterating
- Read
plans/events-on-sx.md— Phase queue + Progress log + Blockers. ls lib/events/— pick up from the most advanced file.- If
lib/events/tests/*.sxexist, run them viabash lib/events/conformance.sh. Green before new work. - Read
lib/datalog/datalog.sxpublic API once — that's your engine. - Check substrate readiness:
bash lib/datalog/conformance.sh— must be greenlib/persist/persist.sx— if missing, Phase 2 transactional booking is blocked (note in Blockers; Phase 1 can still proceed without it)lib/flow/flow.sx— if missing, Phase 3 notification flows blocked
The queue
Phase order per plans/events-on-sx.md:
- Phase 1 — calendar facts + RRULE expansion + availability rules
- Phase 2 — transactional booking (capacity-safe, persist-backed)
- Phase 3 — notification delivery flows (reminders, digests, retry)
- Phase 4 — federation: cross-instance calendars
Within a phase, pick the checkbox that unlocks the most tests per effort.
Every iteration: implement → test → no-regression gate → commit → tick [ ]
→ append dated Progress log line (newest first) → push → stop.
Ground rules (hard)
- Scope: only
lib/events/**andplans/events-on-sx.md. Do NOT editspec/,hosts/,shared/,lib/datalog/,lib/persist/,lib/flow/,lib/stdlib.sx, orlib/root. May import fromlib/datalog/, and once they existlib/persist/+lib/flow/. - NEVER call
sx_build. 600s watchdog. If sx_server binary broken → Blockers entry, stop. - Capacity safety is the contract. Two concurrent bookings on the last seat must NEVER both succeed. Test the race explicitly. The capacity-check + append-event must be atomic at the persist boundary; if persist doesn't expose that primitive, that's a substrate Blockers entry, not a workaround.
- Recurrence is bounded. Never expand an RRULE without a window. An unbounded RRULE expansion is an infinite computation. Every API entry that takes an event takes a (start, end) window too.
- Notifications are at-least-once. Idempotency keys on every digest / reminder. A retry must not double-deliver a sent message; the recipient shouldn't see two "your event starts in 1h" pings.
- Shared-substrate issues (problem in datalog / persist / flow) → Blockers entry with minimal repro. Do NOT patch around it.
- SX files:
sx-treeMCP tools ONLY.sx_validateafter edits. - Worktree: commit, push to
origin/loops/events. Never touchmainorarchitecture. - Commit granularity: one feature per commit. Short factual messages
(
events: weekly RRULE expansion + 14 tests). - Plan file: update Progress log + tick boxes every commit.
Events-specific gotchas
- RRULE is well-trodden. RFC 5545 defines the grammar; don't invent a new recurrence DSL. Implement the subset rose-ash actually needs (daily, weekly, monthly with byday, until/count) and explicitly defer the rest.
- Availability is constraint propagation. Free/busy = forward-chained Datalog: occurrence facts within window + booking facts + per-attendee conflict rules. The same query answers "is X free?" and "when is X next free?" (same rules, different bindings).
- Capacity ≠ availability. Availability is per-actor; capacity is
per-event. A room with 50 seats has 50 capacity; the room "available" to
the 51st booker is
falseeven though the room exists. Encode capacity as a separate fact, not as an attribute of availability. - Notification delivery is shared with feed/notify. Don't build a
bespoke email/push transport — inject one and design the interface
generically. When
feed-on-sxlands its notify path, the two consumers should share a transport. Flag in Progress log when you build a shape that's a candidate fordelivery-on-sxextraction. - Paid tickets cross subsystems. A paid booking calls into
commerce-on-sxcheckout flow. Don't import commerce here — define the contract (request, callback shape) and have commerce import you (or both import a contract module).
General gotchas (all loops)
- SX
do= R7RS iteration. Usebeginfor multi-expr sequences. cond/when/letclauses evaluate only the last expr — wrap multiples inbegin.env-bind!creates a binding;env-set!mutates an existing one (walks scope chain).sx_validateafter every structural edit.list?returns false on raw JS Arrays — host data must be SX-converted.
Style
- No comments in
.sxunless non-obvious. - No new planning docs — update
plans/events-on-sx.mdinline. - Short, factual commit messages.
- One feature per iteration. Commit. Log. Push. Next.
Go. Start by reading the plan; find the first unchecked [ ]; implement it.