# 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 `flow`s 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 1. Read `plans/events-on-sx.md` — Phase queue + Progress log + Blockers. 2. `ls lib/events/` — pick up from the most advanced file. 3. If `lib/events/tests/*.sx` exist, run them via `bash lib/events/conformance.sh`. Green before new work. 4. Read `lib/datalog/datalog.sx` public API once — that's your engine. 5. Check substrate readiness: - `bash lib/datalog/conformance.sh` — must be green - `lib/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/**` and `plans/events-on-sx.md`. Do NOT edit `spec/`, `hosts/`, `shared/`, `lib/datalog/`, `lib/persist/`, `lib/flow/`, `lib/stdlib.sx`, or `lib/` root. May **import** from `lib/datalog/`, and once they exist `lib/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-tree` MCP tools ONLY. `sx_validate` after edits. - **Worktree:** commit, push to `origin/loops/events`. Never touch `main` or `architecture`. - **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 `false` even 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-sx` lands its notify path, the two consumers should share a transport. Flag in Progress log when you build a shape that's a candidate for `delivery-on-sx` extraction. - **Paid tickets cross subsystems.** A paid booking calls into `commerce-on-sx` checkout 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. Use `begin` for multi-expr sequences. - `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`. - `env-bind!` creates a binding; `env-set!` mutates an existing one (walks scope chain). - `sx_validate` after every structural edit. - `list?` returns false on raw JS Arrays — host data must be SX-converted. ## Style - No comments in `.sx` unless non-obvious. - No new planning docs — update `plans/events-on-sx.md` inline. - Short, factual commit messages. - One feature per iteration. Commit. Log. Push. Next. Go. Start by reading the plan; find the first unchecked `[ ]`; implement it.