@odla-ai/chapter 0.36.0 → 0.37.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
@@ -404,6 +404,112 @@ Simulation is deliberately a UI/test boundary, not acceptance evidence for a
404
404
  live Chapter. A follower still has to prove its real application write, Stripe
405
405
  webhook, and calendar booking journey before production.
406
406
 
407
+ ### Authenticated member pages
408
+
409
+ Enable the standard member catalog explicitly:
410
+
411
+ ```ts
412
+ const chapter = defineChapter({
413
+ // ...normal chapter config...
414
+ memberContent: {
415
+ localAuthoring: true,
416
+ parent: {
417
+ sourceId: "built-not-found",
418
+ // default follower vault key: member_content_control_secret
419
+ },
420
+ },
421
+ });
422
+ ```
423
+
424
+ This creates two deliberately separate authoring layers. The Parent delivers
425
+ immutable signed revisions; the Chapter admin edits one local revision. The
426
+ effective catalog merges them without permitting a Parent replay to replace a
427
+ Chapter page. Each page declares a stable id/slug, navigation section/order,
428
+ safe Markdown, and either `all-members` or an explicit set of tier ids. Tier
429
+ sets are not treated as an ordinal ladder.
430
+
431
+ ```ts
432
+ const parentDocument = {
433
+ schemaVersion: 1,
434
+ revision: 4,
435
+ generatedAt: Date.now(),
436
+ source: { type: "parent", appId: "built-not-found" },
437
+ target: { chapterId: "example-chapter", environment: "dev" },
438
+ pages: [{
439
+ id: "steward-library",
440
+ slug: "steward-library",
441
+ title: "Steward library",
442
+ navigation: { section: "Network", order: 20 },
443
+ audience: { kind: "tiers", tierIds: ["steward"] },
444
+ teaserMarkdown: "Advanced material for stewards.",
445
+ bodyMarkdown: "The **private** operating library.",
446
+ // Built Not Found delegates only this offer to the Chapter admin.
447
+ upsell: { owner: "chapter" },
448
+ }],
449
+ };
450
+ ```
451
+
452
+ An upsell references only a live tier id and bounded copy. Authored content
453
+ cannot set price, Stripe ids, checkout state, HTML, CSS, or arbitrary links.
454
+ The Worker attaches the currently offerable tier name and price. A Parent page
455
+ may own its offer (`owner: "parent"`) or declare `owner: "chapter"`; the local
456
+ document then uses `upsellOverrides` for that exact page. Chapter-authored
457
+ pages always keep Chapter offer authority.
458
+
459
+ `GET /api/member-content` requires a signed-in member. Chapter resolves the
460
+ member's tier from their local application and removes every unauthorized
461
+ `bodyMarkdown` before serializing JSON. Locked metadata may include a teaser
462
+ and resolved offer. Admins may preview every body through the same projection.
463
+ Client-side hiding is not an authorization boundary.
464
+
465
+ Render the projection through the stock member area:
466
+
467
+ ```tsx
468
+ <MembersArea api={api} signOut={signOut} memberPages />
469
+ ```
470
+
471
+ Or compose `MemberContentArea` directly and provide a host-owned upgrade URL or
472
+ callback. The package does not pretend a second application is an upgrade:
473
+
474
+ ```tsx
475
+ <MemberContentArea
476
+ api={api}
477
+ upsellHref={(tierId) => approvedUpgradeRoute(tierId)}
478
+ />
479
+ ```
480
+
481
+ The standard Chapter admin keeps the top-level Dashboard/People/Settings
482
+ grammar and adds a **Member content** tab under Settings. It labels inherited
483
+ pages as read-only, shows source/access/offer ownership, and edits the complete
484
+ local JSON contract with revision guards and actionable validation errors.
485
+ Agents may use the same authenticated `/api/admin/member-content` GET/PUT
486
+ contract. Page bodies remain out of CRM; CRM continues to own people and
487
+ lifecycle state.
488
+
489
+ For central delivery, pair `createMemberContentEnvelope(document)` with
490
+ `deliverMemberContent` from `@odla-ai/chapter/worker`. The dedicated signed
491
+ route rejects wrong source/target/environment, stale or conflicting revisions,
492
+ bad digests, undeclared tiers, and any Parent revision that would invalidate
493
+ existing Chapter-owned content. The CRM edge and signup-control secret are
494
+ never reused.
495
+
496
+ Agents can run the same contract through the supported CLI. `tiers.json`
497
+ contains a JSON array of the live safe authority projection (`id`, `name`,
498
+ `priceCents`, and `active`) and deliberately excludes provider Price ids:
499
+
500
+ ```bash
501
+ odla-ai chapter member-content validate --file parent.json --tiers tiers.json --json
502
+ odla-ai chapter member-content compose --parent parent.json --chapter chapter.json --tiers tiers.json --json
503
+ odla-ai chapter member-content project --parent parent.json --chapter chapter.json --tiers tiers.json --tier community --json
504
+ odla-ai chapter member-content deliver --file parent.json --tiers tiers.json \
505
+ --url https://chapter.example --from-env MEMBER_CONTENT_SECRET --json
506
+ ```
507
+
508
+ Use `--stdin` instead of `--from-env` when the signing secret comes from a
509
+ producer process. The value is never accepted as an argument. `project` is the
510
+ server-disclosure proof and omits locked bodies; `compose` is a local operator
511
+ authoring view and contains the supplied content.
512
+
407
513
  ### Hub-authoritative live signup control
408
514
 
409
515
  When a hub must control the live membership page as well as preview it, use the