@odla-ai/chapter 0.54.1 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1520,7 +1520,7 @@ These bite silently — a smoke test won't catch them:
1520
1520
  | `submitConfirmation` | `POST /api/applications` (submit) — a free tier always; a paid tier only when `paymentConfirmation` is disabled, so a paid applicant is confirmed once | template `enabled` flag |
1521
1521
  | `prepEmail` | `POST /api/schedule/book` — once per application; a reschedule sends no second one | template `enabled` flag |
1522
1522
  | `paymentConfirmation` | Stripe webhook, first successful invoice | template `enabled` flag |
1523
- | `seatAccepted` | `POST /api/named-seat/accept` — to the seat's purchaser, once per seat, naming the recipient and tier | template `enabled` flag; the seeded default copy applies until the group row carries its own `emailTemplates.seatAccepted` |
1523
+ | `seatAccepted` | `POST /api/named-seat/accept` — to the seat's purchaser, once per seat, naming the recipient and tier | explicit `enabled: true` on a stored `emailTemplates.seatAccepted`; missing copy blocks delivery |
1524
1524
 
1525
1525
  ```ts
1526
1526
  defineChapter({ /* … */ sends: { adminNotification: "payment" } });
@@ -1528,8 +1528,19 @@ These bite silently — a smoke test won't catch them:
1528
1528
 
1529
1529
  **Template variables.** Every value comes from stored data — the frozen
1530
1530
  checkout the applicant consented to, the tier catalog, the booked meeting,
1531
- the request origin — never from a price or a name written into code. An
1532
- unknown placeholder renders empty, so copy can use any subset.
1531
+ the request origin — never from a price or a name written into code. Sends
1532
+ reject unknown placeholders, missing required values, internal draft notes,
1533
+ incomplete copy, and unresolved tier display names. Optional blanks must be
1534
+ declared by the event (for example, a free tier’s payment amount suffix).
1535
+
1536
+ **Readiness on page open.** The Email page checks every built-in and host event
1537
+ against saved templates and current tier names. Missing templates and invalid
1538
+ values appear with the event’s trigger and recipient. Explicitly disabled
1539
+ emails stay off. `emails.events` declares custom reminder contracts; pass the
1540
+ same event to `sendTemplated`. Gift invitations and acceptance notices require
1541
+ stored copy; delivery never supplies a hidden fallback. Hub mode uses the
1542
+ site’s actual CRM template registry and CRM settings. All automatic sends
1543
+ require explicit `enabled: true`; validation never enables a template.
1533
1544
 
1534
1545
  | Variable | Templates | Value |
1535
1546
  |---|---|---|
@@ -1734,6 +1745,38 @@ These bite silently — a smoke test won't catch them:
1734
1745
  durable, unfinished local approval work without starving later applicants.
1735
1746
  Neither helper installs a cron or provisions infrastructure by itself.
1736
1747
 
1748
+ Starting with Chapter 0.55.0, a named-seat-enabled `JoinIsland` also offers the
1749
+ additional member in the initial payment step. Both journeys read
1750
+ `seatPriceCents` from the selected SKU's frozen offering: a positive amount is
1751
+ the additional-member rate, and zero means included. No tier names or prices
1752
+ are hardcoded. A host form may pass `payment.initialNamedSeat` with
1753
+ `{ recipientName, recipientEmail }`; Chapter validates that selection and
1754
+ shows a fresh total before consent. Custom `payment.renderPriceLines` slots
1755
+ receive `namedSeatCents` (including zero); `dueTodayCents` already includes it.
1756
+
1757
+ `POST /api/payments/quote?application=<id>` accepts
1758
+ `{ namedSeat: { recipientName, recipientEmail } }` or `{ namedSeat: null }`
1759
+ before checkout starts. The response binds the selection, configured amount,
1760
+ and combined renewal total into its digest. The subscription request requires
1761
+ `seatTermsAck: true` for a selected seat, alongside the quote digest and refund
1762
+ consent. After that, the selection is immutable. The
1763
+ `x-chapter-checkout-started` response header tells a resumed UI to lock edits.
1764
+ There is one subscription and one initial payment. A priced seat has a
1765
+ separate, co-terminating recurring item; membership discounts are restricted
1766
+ to the primary product. An included seat has no provider item or extra charge.
1767
+ Independent invoice evidence commits the primary purchase and named seat
1768
+ together. A seat-only refund returns only its allocation and removes only its
1769
+ renewal item; a full membership refund covers both without a second refund.
1770
+
1771
+ Upgrade and provision the network owner's authority integration before
1772
+ deploying the follower on 0.55.0. The added checkout `namedSeat` and seat
1773
+ `combinedInvoice`/`purchaserEmailNormalized` fields are optional; a signup
1774
+ purchaser need not have a user account yet, so `purchaserUserId` is optional.
1775
+ The existing authority peer's configured `seatPriceId` is still server-owned
1776
+ and must match a positive offering seat price on a separate Stripe product.
1777
+ In-process users of `bindMembershipCheckout` pass that configured Price as
1778
+ its fourth argument when binding a selected seat.
1779
+
1737
1780
  The member area exposes the authenticated purchaser's named-seat checkout
1738
1781
  only when configured, and as soon as the membership is paid: `/api/me`
1739
1782
  reports `namedSeatsEnabled` for a paid application whose interview is still