@odla-ai/chapter 0.25.0 → 0.25.2

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.
@@ -49,8 +49,24 @@ runtime and data rollback remain proven.
49
49
 
50
50
  ## Phase 0 — Freeze the operating baseline
51
51
 
52
- Create or extend root `MIGRATION.md`; never replace its history. Record state
53
- `inventoried` only after the human approves the baseline.
52
+ Use odla PM as the migration's durable coordination state; do not create a
53
+ parallel migration diary. If the odla app already exists, begin by listing
54
+ doing tasks, open bugs, open goals, and recent decisions for the `appId` from
55
+ `odla.config.mjs`. Read the active task, its linked goal, and their comments
56
+ before inspecting code.
57
+
58
+ If the app is not registered yet, choose the intended stable `appId`, keep
59
+ Phase 0/early provisioning evidence in focused commits and checkpoint
60
+ handoffs, and rerun the inventory when a fresh agent resumes. Old branches,
61
+ git history, unrelated deployments, and earlier experiments are only leads to
62
+ verify. Immediately after the first provision registers the app, create PM
63
+ goals/tasks/decisions and backfill the approved baseline evidence; PM is
64
+ authoritative from then on.
65
+
66
+ Use one conformance goal per acceptance outcome, parallel tasks for independent
67
+ tracks, decisions for product/data/identity/route/rollback choices, and bugs
68
+ for defects. Comments carry the source commit, immutable deployment version,
69
+ verification command/result, and rollback checkpoint. Never put secrets in PM.
54
70
 
55
71
  ### Inventory the product
56
72
 
@@ -68,6 +84,16 @@ Create or extend root `MIGRATION.md`; never replace its history. Record state
68
84
  render sink;
69
85
  - telemetry, freshness/error alerts, deploy commands, and CI auto-deploys.
70
86
 
87
+ For every route, record the method/path, current consumer, owning implementation
88
+ (`legacy`, Chapter, or an explicit host adapter), request and response schemas,
89
+ status/error/auth/cache behavior, and its evidence. Source evidence names the
90
+ checked-in path plus commit; deployment evidence names the origin plus immutable
91
+ Worker/deployment version. A response observed on an unversioned deployment
92
+ describes only that deployed build at that moment. It does not establish route
93
+ ownership or the contract of the branch being changed. Treat old branches, git
94
+ history, and unrelated deployments as leads to verify, never as the migration
95
+ specification.
96
+
71
97
  ### Freeze visual and behavior evidence
72
98
 
73
99
  Capture representative production screenshots and measured layouts at 390,
@@ -150,13 +176,21 @@ explicit:
150
176
 
151
177
  Before deleting old schema/rules/provisioning:
152
178
 
153
- 1. freeze the legacy schema and rules as reviewed test fixtures;
154
- 2. normalize only approved representational differences;
155
- 3. compare `createChapterIntegration(chapter)` namespaces, attrs, links, rules,
156
- and seeds to the fixtures;
157
- 4. fail on missing fields, widened rules, cardinality changes, renamed natural
179
+ 1. freeze the legacy schema and rules from an exact reviewed source path and
180
+ commit, with a recorded content digest;
181
+ 2. make that baseline independent: use the untouched legacy source or a literal
182
+ fixture that does not import Chapter, the active descriptor/schema alias, or
183
+ another generated candidate; do not modify baseline and candidate in the
184
+ same approval;
185
+ 3. normalize only approved representational differences;
186
+ 4. compare `createChapterIntegration(chapter)` namespaces, attrs, links, rules,
187
+ and seeds to the independent baseline;
188
+ 5. run a negative control by removing or changing a known field/rule on one side
189
+ and prove the parity test fails before trusting a green result;
190
+ 6. fail on missing fields, widened rules, cardinality changes, renamed natural
158
191
  keys, new client-readable data, or unapproved seed differences;
159
- 5. record every intentional delta and migration mapping in `MIGRATION.md`.
192
+ 7. record every intentional delta and migration mapping as a PM decision, and
193
+ attach the comparator evidence to the linked conformance goal/task.
160
194
 
161
195
  `createChapterIntegration()` inserts the group seed only when absent. Existing
162
196
  runtime owner edits do not update from later config changes. Compare the current
@@ -343,10 +377,25 @@ Do not replace schema, worker, routes, and UI in one unreviewable change.
343
377
  routes run before built-ins and receive Chapter's existing context
344
378
  (`verifyUser`, `makeDb`, `roleFor`, `isAdmin`); do not verify JWTs twice.
345
379
  3. Initially keep every bespoke endpoint as a host route. Alias legacy URLs to
346
- Chapter behavior instead of breaking clients.
347
- 4. Compare built-in and legacy behavior route by route; only then remove a host
348
- route that Chapter fully owns.
349
- 5. Keep static fallback and API handling explicit so unknown APIs cannot fall
380
+ Chapter behavior only when the route-contract matrix proves method, auth,
381
+ request body, response status, JSON keys/types/nesting, units, null/omission
382
+ semantics, and relevant headers are compatible. Matching business values
383
+ alone is not response-shape parity.
384
+ 4. When contracts differ, keep the legacy route until its consumer adopts the
385
+ packaged Chapter component end to end, or add an explicit adapter with
386
+ contract tests. Do not simply repoint the existing browser code.
387
+ 5. In Chapter 0.25, `/api/join-config` is Chapter-owned and returns the
388
+ group-scoped join configuration, raw cent values, policy copy, and readiness;
389
+ it is not a promise to reproduce a legacy route's prebuilt Stripe line items
390
+ or publishable key. `JoinIsland` delegates payment to `PaymentStep`, which
391
+ obtains `clientSecret`, `publishableKey`, and `lineItems` from
392
+ `POST /api/payments/subscription` after application submission and refund
393
+ acknowledgement. Verify this installed-version contract before retiring a
394
+ legacy join route.
395
+ 6. Compare built-in and legacy behavior route by route; only then remove a host
396
+ route that Chapter fully owns and whose consumers use the verified Chapter
397
+ contract or adapter.
398
+ 7. Keep static fallback and API handling explicit so unknown APIs cannot fall
350
399
  through to a successful SPA document.
351
400
 
352
401
  Test public/private method policies, body limits, idempotency, auth 401 vs 403,
@@ -388,10 +437,15 @@ fixture. Compare markup/behavior and the Phase 0 screenshots before deletion.
388
437
  `renderHeader` when the header belongs inside the Chapter scope. Chapter mode
389
438
  defaults to Dashboard, People, and Settings; move former global Billing,
390
439
  Calendar, Email, and per-record operations into nested tabs.
391
- - Preserve a distinctive People summary, rail, and record heading with
392
- `collectionSection`'s `renderSummary`, `renderMaster`, and
393
- `renderDetailHeader` slots. Replace specialized record tabs rather than
394
- flattening them into global navigation.
440
+ - Preserve distinctive People summary content, list presentation, record
441
+ heading, and specialized panels with `collectionSection`'s `renderSummary`,
442
+ `renderMaster`, `renderDetailHeader`, and record-tab slots. These slots
443
+ customize content; they do not fix pane geometry.
444
+ - Operational usability is not pixel parity with a cramped legacy admin. At
445
+ desktop widths, the unselected People collection must use the full workspace
446
+ width; after a record opens it becomes master/detail. This is the
447
+ `collapseClosedDetail` default. Set it to `false` only when the empty detail
448
+ pane contains intentional, useful content approved by the human.
395
449
  - Application-backed pipeline changes must use a lifecycle adapter that invokes
396
450
  the authoritative application endpoints. Prove approve/refund/manual
397
451
  transition side effects; a raw CRM `setStage` is not parity.
@@ -416,6 +470,9 @@ At 390, 768, 1280, and 1440 pixels in every theme, compare:
416
470
  - wordmark, text baseline, theme control, active state, and hover/focus;
417
471
  - join fields/errors, payment and slot picker, Clerk sign-in containment;
418
472
  - member cards/actions and every admin section/table/drawer;
473
+ - at 1280 and 1440, every unselected collection table uses the full content
474
+ width: no empty detail pane and no wide table squeezed into a narrow rail;
475
+ after selection, both master and detail remain usable;
419
476
  - no clipped labels, card text escape, accidental document scroll, or
420
477
  horizontal overflow;
421
478
  - loading, empty, error, unauthorized, 404, and reduced-motion states.
@@ -427,16 +484,21 @@ not visual parity.
427
484
 
428
485
  ### Clerk
429
486
 
430
- Use the same Clerk application/workspace. Chapter claim mode requires session
431
- claims for both email and role; hub table mode uses lowercase `admins` rows.
432
- Prove existing allowed/forbidden users, role ladder, super-admin behavior,
433
- malformed/expired sessions, sign-out, and return targets.
434
-
435
- If the legacy verifier enforces JWT `aud` or `azp`, keep that host verifier in
436
- front of Chapter. This Chapter release verifies issuer but does not expose or
437
- enforce the CLI-provisioned Clerk audience. Add wrong-audience and
438
- wrong-authorized-party fixtures; do not cut over auth until the legacy contract
439
- still rejects them.
487
+ For the one website being converted, retain that website's existing Clerk
488
+ application/workspace unless the human approves an identity migration. A
489
+ leader and follower are different websites and use different Clerk
490
+ applications, publishable keys, issuers, roles, and local session tokens. A
491
+ Clerk JWT never authenticates a cross-site delivery.
492
+
493
+ Chapter claim mode requires session claims for both email and role; hub table
494
+ mode uses lowercase `admins` rows. Prove signed-out, allowed, forbidden,
495
+ provisional, member, admin, super-admin, malformed, expired, wrong-issuer,
496
+ sign-out, and return-target behavior. If the site's verifier intentionally
497
+ enforces JWT `aud` or `azp`, retain that check with a host verifier until
498
+ Chapter exposes the same policy and add wrong-audience/wrong-authorized-party
499
+ fixtures. If it never enforced those claims, mark those two cases `n/a` only
500
+ after the full matrix is recorded; do not call their absence an auth success or
501
+ use it to skip the remaining authorization tests.
440
502
 
441
503
  When Chapter needs server-side account/role operations, set the named
442
504
  app-readable `clerk_secret_key` through:
@@ -493,9 +555,12 @@ selected account invitation effect. Never let dev send to imported members.
493
555
 
494
556
  Check in an executable acceptance manifest with:
495
557
 
496
- - actual dev URLs and Worker version ids;
558
+ - actual dev URLs and immutable Worker version ids, paired with the source
559
+ commit they are expected to run;
497
560
  - deterministic synthetic member/application ids;
498
- - expected routes/statuses, schema/counts/freshness, and Chapter backend marker;
561
+ - expected route owners and full request/response contracts—not only paths,
562
+ statuses, or equal values—plus schema/counts/freshness and Chapter backend
563
+ marker;
499
564
  - expected migration-readiness inputs and a `200` only when each is green;
500
565
  - expected account, payment, booking, email, CRM, and admin outcomes;
501
566
  - expected o11y service/release and safe trace;
@@ -521,6 +586,14 @@ Run the real journey:
521
586
  `doctor`, unit tests, build, CLI smoke, a login page, and a human-looking admin
522
587
  screen are necessary but not sufficient.
523
588
 
589
+ Before filing a route bug from deployed evidence, prove that the probed
590
+ deployment runs the intended commit/version. Repeat the request against the
591
+ direct candidate origin and the public domain with a unique non-secret query
592
+ value, capture `Age`, `Cache-Control`, `CF-Cache-Status`, `ETag`, and the Worker
593
+ version marker, and re-test after the documented edge/config propagation
594
+ window. A cached pre-deploy or pre-credential `404` is not evidence that the
595
+ current route is absent.
596
+
524
597
  ## Phase 8 — Production parallel run and cutover
525
598
 
526
599
  Do not add production until the human approves the full dev report and both
@@ -565,6 +638,7 @@ Keep both rollback capabilities through the approved observation window
565
638
  webhook, job, and secret only in a separate human-approved change with a fresh
566
639
  journey afterward.
567
640
 
568
- Close `MIGRATION.md` only when every applicable track is `primary-prod` or
569
- `retired`, the observation window passes, and the human confirms the existing
570
- product—not merely a Chapter canary—has been converted.
641
+ Mark the migration goals met and tasks done only when every applicable track is
642
+ `primary-prod` or `retired`, the observation window passes, evidence is attached
643
+ in PM, and the human confirms the existing product—not merely a Chapter
644
+ canary—has been converted.
@@ -67,9 +67,19 @@ consumer, not a visual identity to rename.
67
67
 
68
68
  ## Phase 0 — Resolve the product and brand brief
69
69
 
70
- Write `PRODUCT.md` before code. Record decisions rather than inventing business
71
- facts. Ask the human only for missing choices that change money, identity,
72
- outbound communication, authorization, or the public brand.
70
+ Record decisions rather than inventing business facts. Ask the human only for
71
+ missing choices that change money, identity, outbound communication,
72
+ authorization, or the public brand. Do not create a parallel product or
73
+ migration diary.
74
+
75
+ PM requires a registered, co-owned odla app. Before Phase 3 registration, use a
76
+ focused branch/commit and checkpoint handoff for the approved brief and rerun
77
+ the Phase 0 checks when a fresh agent resumes. Immediately after provision
78
+ registers the app, create PM conformance goals and track tasks, record these
79
+ product/brand/identity decisions, and backfill the earlier evidence. PM is
80
+ authoritative from then on; start every later session by listing doing tasks,
81
+ open bugs/goals, and recent decisions for the `appId` in
82
+ `odla.config.mjs`. Never put secrets in PM.
73
83
 
74
84
  | Decision | Required answer |
75
85
  | --- | --- |
@@ -158,7 +168,6 @@ Recommended minimum tree:
158
168
 
159
169
  ```text
160
170
  .
161
- ├── PRODUCT.md
162
171
  ├── package.json
163
172
  ├── index.html
164
173
  ├── vite.config.mjs
@@ -344,9 +353,8 @@ the private profile write and records `clerkPrivateMetadataSyncedAt`.
344
353
  `copy` is a recursive partial of `ChapterCopy`. `defineChapter()` resolves it
345
354
  to a complete `chapter.copy`, which is the voice source for packaged join,
346
355
  member, and admin surfaces. Put plain text in `copy`; use render slots for
347
- markup or a different composition. Include the voice constraints from
348
- `PRODUCT.md` in a contract test, such as banned punctuation or required
349
- terminology.
356
+ markup or a different composition. Include the approved voice constraints in a
357
+ contract test, such as banned punctuation or required terminology.
350
358
 
351
359
  In this release, `prices.currency` and `prices.interval` record intent but do
352
360
  not control the group row, public join config, or Stripe charge. The Stripe
@@ -376,6 +384,32 @@ Snapshot these resolved values in `chapter-contract.test.ts`:
376
384
  schema/rules/seeds/probes, using the fixed clock because the integration seed
377
385
  carries the timestamp.
378
386
 
387
+ ### When this site is a leader or follower
388
+
389
+ Model the network as website nodes joined by directed delivery edges. Every
390
+ website keeps its own `appId`, ODLA tenant/key, CRM config, Clerk application,
391
+ issuer, roles, and users. A leader edge is one `network.targets[]` record with
392
+ the follower id/name/origin, optional vault secret name, and a per-record-type
393
+ field allowlist. The follower declares compatible CRM types and fields; it does
394
+ not need the leader's Clerk configuration.
395
+
396
+ The leader browser authenticates only to the leader. Its Worker authorizes the
397
+ local admin, reads the edge's share secret from the leader vault, and sends a
398
+ versioned `{ version, type, hubRecordId, input }` payload to the follower. The
399
+ follower validates that share secret and its local CRM config, then writes with
400
+ its own `ODLA_API_KEY`. Never send either website's Clerk JWT or ODLA key across
401
+ the edge. Pipeline, account, billing, and roles remain authoritative locally.
402
+
403
+ Conformance must prove:
404
+
405
+ - the target metadata returned to the browser contains no secret;
406
+ - only allowlisted fields leave the leader;
407
+ - unknown follower types/fields fail before a write;
408
+ - wrong share secret returns `401` and writes nothing;
409
+ - retry updates the provenance-matched follower record instead of duplicating;
410
+ - neither Clerk token nor either `ODLA_API_KEY` appears in the cross-site
411
+ request or client bundle.
412
+
379
413
  ## Phase 3 — Provisioning and Worker shell
380
414
 
381
415
  ```js
@@ -448,6 +482,10 @@ separate browser-owned booking-consent checkpoint for availability plus
448
482
  create/reschedule/cancel access. A grant created for the retired read-only
449
483
  mirror must be reconnected. No OAuth code or token passes through the agent.
450
484
 
485
+ The app now exists. Initialize PM, backfill the approved Phase 0–2 decisions
486
+ and evidence, create linked tasks/goals for the remaining tracks, and read that
487
+ state before continuing.
488
+
451
489
  ## Phase 4 — Build the four application surfaces
452
490
 
453
491
  Import the public site's application theme once. For the admin application,
@@ -564,6 +602,13 @@ anchors; record details are record-tab anchors. Do not promote those tabs into
564
602
  the global site header. Use `chrome="standalone"` only for an admin-only product
565
603
  that has no host shell.
566
604
 
605
+ The People collection uses the full workspace width until a record is selected,
606
+ then becomes master/detail. Keep the default
607
+ `collectionSection({ collapseClosedDetail: true })`; set it to `false` only
608
+ when an intentional empty-detail panel contains useful approved content. At
609
+ 1280 and 1440 pixels, verify a wide collection table is not squeezed into a
610
+ narrow rail beside an empty pane.
611
+
567
612
  The router must serve and reload `/`, every public page, `/join`, `/members/`,
568
613
  and `/admin/` without accidentally sending API paths to the SPA. Chapter
569
614
  terminates unknown `/api/*` paths as JSON 404s before the static asset fallback.
@@ -571,8 +616,10 @@ terminates unknown `/api/*` paths as JSON 404s before the static asset fallback.
571
616
  ## Phase 5 — Configure Clerk and bootstrap authority
572
617
 
573
618
  At this step, read Clerk's current agent-facing CLI documentation. Use one
574
- Clerk application with development and production instances; do not create a
575
- second application for production.
619
+ Clerk application with development and production instances **for this
620
+ website**; do not create a second application merely for this site's production
621
+ environment. A leader and a follower are separate websites and use separate
622
+ Clerk applications, issuers, users, and roles.
576
623
 
577
624
  For default chapter claim auth, the session token must include both:
578
625