@odla-ai/chapter 0.15.1 → 0.17.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
@@ -235,22 +235,37 @@ These bite silently — a smoke test won't catch them:
235
235
  - **`--legacy-peer-deps` is a diagnostic, not a setting.** It suppresses exactly
236
236
  the peer conflict that tells you a pair is unsupported. If you need it, find out
237
237
  why first.
238
- - **The admin surface is intentionally minimal, and staying that way.** chapter
239
- ships `/api/admin/scheduling` (booking rules, with field-keyed validation
240
- messages) and `/api/admin/meetings` (the reconciled agenda, with the applicant
241
- joined, `?all=1`, `from`/`to`, drift fields, Meet/Calendar links and the group
242
- timezone), plus `peopleSection`. **Dashboard, billing, email config/log/test,
243
- per-person comms, people list, role changes, approve and refund are out of
244
- scope** they are yours as host routes via the seam. Don't plan around them
245
- shipping.
246
- - **`onboardingInvite` is a template with no built-in caller.** It is seeded into
247
- the group's email settings (so you can edit its copy) but chapter never fires
248
- it, because the moment it would fire approval is a host route (approval is
249
- out of scope, above). Send it from your own approve handler via `sendTemplated`;
250
- the seed existing does not mean the built-ins send it.
251
- - **Server-side Clerk is exported, even though the admin *routes* aren't.** A role
252
- change is your host route — but you don't hand-roll the Clerk REST calls for it.
253
- Beside `createClerkUser`/`createClerkInvitation`/`canChangeRole`, chapter exports
238
+ - **The admin operational surface ships (0.16.0).** `/api/admin/*` is now a full
239
+ membership-operations API, admin-gated (`verifyUser` + `isAdmin`):
240
+ - **Roster/identity:** `GET /people` (union of `$users` + applications by email,
241
+ roles from `clerkListUsers`), `GET /people/access`, `POST /people/role`,
242
+ `POST /crm/sync` (backfill/reproject).
243
+ - **Pipeline/meetings:** `GET /dashboard` (flow counts, stage counts + weekly
244
+ delta, agenda, live revenue), `GET /meetings` (reconciled agenda),
245
+ `GET/PUT /scheduling`, `POST /meetings/:id/{reschedule,cancel}`,
246
+ `PATCH /applications/:id`.
247
+ - **Money:** `GET /billing` (applications live Stripe subs; `truncated` flags a
248
+ >100 page instead of losing rows; `billingReady:false` when no Stripe key is
249
+ vaulted), `POST /applications/:id/{approve,refund}`.
250
+ - **Email:** `GET/PUT /group/email`, `GET /email/log`, `POST /email/test`,
251
+ `GET /people/:id/comms` (sent mail + reconstructed calendar invitations).
252
+
253
+ Two behaviours are **config, not code** — the mechanics are chapter's, the
254
+ decisions are yours (same model as `sends`):
255
+
256
+ ```ts
257
+ defineChapter({ operations: {
258
+ onApprove: { promoteTo: "member", send: "onboardingInvite" }, // promoteTo defaults to the rung below admin; either can be false
259
+ refund: { allowedFrom: ["paid_pending_vetting"], cancelSubscription: true }, // allowedFrom validated against the pipeline
260
+ }});
261
+ ```
262
+
263
+ The escalation rules (super-admin tier, self-demotion lockout) are **not** seams
264
+ — `canChangeRole` package-enforces them so a site can't weaken them. `approve`
265
+ is the caller `onboardingInvite` was missing.
266
+ - **The server-side Clerk primitives are exported too** — for host routes beyond
267
+ the built-in `/people/role`. Beside
268
+ `createClerkUser`/`createClerkInvitation`/`canChangeRole`, chapter exports
254
269
  the odla→Clerk write half: `clerkGetUserByEmail`, `clerkGetUser`, `clerkListUsers`
255
270
  (auto-paginated — never a silent 100-user cap), and `clerkSetRole`. Two
256
271
  load-bearing semantics: an absent `public_metadata.role` reads as the lowest rung