Transcript of the real run
One run of scripts/demo.mjs, on 2026-10-10, against the example's own docker compose stack
(PostgreSQL 18, Dragonfly, Keycloak 26.8 with the imported realm, Jaeger, GlitchTip), the API and
the worker started from dist/. Tokens are real tokens of the local Keycloak and are never
printed; the passwords of the demo users are their user names, for local development only.
Bodies are cut after 700 characters.
Read with this in mind. This run predates a change of design in
analytics. The occupancy of step 7 was then served by a stored line per calendar and day, recounted at each event by a second projection (process analytics.occupancyandOccupancyRegisterin the trace of step 10) and verified by a nightly job. Since then the occupancy is measured from the outcomes when it is read, and that projection, its table and the job are gone. The run is kept as it was; the last section, "Run again after analytics moved to read time", holds what a second real run answered for these two steps.It also predates the networks of practices (ADR-0003, 2026-10-13). Since then
demo-adminholdsnetwork-adminand opens the practices of the networknorth, the audit trail of step 9 is read bydemo-ops, platform staff in no network, through an elevation the trail records, and every entry carries the network of its actor.scripts/demo.mjsasks it that way; this run was not recorded again.
Commands
Section titled “Commands”cp .env.example .env # then, in .env: OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:54318docker compose --profile identity --profile observability --profile errors up -d --waitdocker compose logs glitchtip | grep 'DSN:' # the DSN goes to SENTRY_DSN in .env (host 127.0.0.1:54800)set -a && . ./.env && set +anpm run db:migratenode dist/src/main.js & # the API, jobs offOTEL_SERVICE_NAME=scheduling-worker node dist/src/apps/worker/main.js & # consumers and scheduled jobsnode --env-file=.env scripts/demo.mjsThe stack answered healthy on 127.0.0.1 ports 54320 (PostgreSQL), 54379 (Dragonfly), 54080 (Keycloak), 54686 and 54318 (Jaeger), 54800 (GlitchTip); the API listened on 54300. This transcript is the second run on the same database (a first run on an empty one passed the same steps): the scenario can be replayed, each run opens two more practices.
What the scenario printed
Section titled “What the scenario printed”1. People register, the platform admin opens two practices
Section titled “1. People register, the platform admin opens two practices”PUT /people/meas demo-admin:200{"id":"00000000-0000-4000-8000-00000000a001"}PUT /people/meas demo-manager:200{"id":"00000000-0000-4000-8000-00000000a002"}PUT /people/meas demo-manager-b:200{"id":"00000000-0000-4000-8000-00000000a007"}PUT /people/meas demo-practitioner:200{"id":"00000000-0000-4000-8000-00000000a003"}PUT /people/meas demo-assistant:200{"id":"00000000-0000-4000-8000-00000000a004"}PUT /people/meas demo-patient:200{"id":"00000000-0000-4000-8000-00000000a005"}PUT /people/meas demo-patient-2:200{"id":"00000000-0000-4000-8000-00000000a006"}PUT /people/meas demo-support:200{"id":"00000000-0000-4000-8000-00000000a008"}POST /practicesas demo-admin:201{"id":"1f235636-8be0-4302-b456-a8796b65cc40"}POST /practicesas demo-admin:201{"id":"e6ad8f95-c118-488b-a623-c38eefe29fe6"}POST /practicesas demo-patient:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission identity:practice.open is not granted","requestId":"4bb23933-dd2a-44ab-8628-26301af4b3b0"}POST /practices/1f235636-8be0-4302-b456-a8796b65cc40/membersas demo-manager:201{"id":"00000000-0000-4000-8000-00000000a003"}POST /practices/1f235636-8be0-4302-b456-a8796b65cc40/membersas demo-manager:201{"id":"00000000-0000-4000-8000-00000000a004"}
2. The practitioner opens a calendar with its availability
Section titled “2. The practitioner opens a calendar with its availability”POST /calendarsas demo-practitioner:201{"id":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c"}PUT /calendars/8ec0b2b3-196a-45ba-920f-ab3713e2dd6c/opening-hoursas demo-practitioner:200{"id":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c"}POST /calendars/8ec0b2b3-196a-45ba-920f-ab3713e2dd6c/appointment-typesas demo-practitioner:201{"id":"a5be6ca2-d996-4737-a92c-99fafb1d481c"}
3. Delegation: the assistant manages the calendars delegated to them, and only what was delegated
Section titled “3. Delegation: the assistant manages the calendars delegated to them, and only what was delegated”POST /appointmentsas demo-assistant:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission scheduling:appointment.book is not granted","requestId":"4e7309a8-44f2-4e49-9edb-cef74acf7141"}POST /practices/1f235636-8be0-4302-b456-a8796b65cc40/desks/00000000-0000-4000-8000-00000000a003/delegationsas demo-practitioner:201{"id":"c628c4d1-cbbd-4829-a35c-0ad11e00e2af"}POST /appointmentsas demo-assistant:201{"id":"aed94000-37e1-4d5f-9072-d4e99845d07a"}PUT /calendars/8ec0b2b3-196a-45ba-920f-ab3713e2dd6c/opening-hoursas demo-assistant:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission scheduling:calendar.set-opening-hours is not granted","requestId":"5843e38c-f3a3-4fd2-99c5-6de43ad92d9f"}
4. A patient books for themselves; the same slot is refused to the next one
Section titled “4. A patient books for themselves; the same slot is refused to the next one”POST /appointmentsas demo-patient:201{"id":"2424c221-de78-40f1-bce0-1de51371ef14"}POST /appointmentsas demo-patient-2:409{"type":"urn:problem:slot-taken","title":"Conflict","status":409,"code":"SLOT_TAKEN","detail":"The slot starting at 2026-10-17T06:00:00.000Z on calendar 8ec0b2b3-196a-45ba-920f-ab3713e2dd6c is already held.","requestId":"82ff83a3-482d-4e5e-af38-9c3f1fe79e8b"}GET /appointments?filter[status]=booked&sort=-startsAtas demo-patient:200{"items":[{"id":"40485b8b-4445-44d2-b183-389032bad003","calendarId":"40f19775-cdbe-4e9a-976e-819b48477386","practiceId":"20026e9e-ba63-4cbf-b8d3-bd2d4a1084bf","practitionerId":"00000000-0000-4000-8000-00000000a003","patientId":"00000000-0000-4000-8000-00000000a005","appointmentTypeId":"c906268a-b92e-423b-845a-65309415bf65","startsAt":"2026-10-17T06:00:00.000Z","endsAt":"2026-10-17T06:30:00.000Z","day":"2026-10-17","status":"booked"},{"id":"2424c221-de78-40f1-bce0-1de51371ef14","calendarId":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c","practiceId":"1f235636-8be0-4302-b456-a8796b65cc40","practitionerId":"00000000-0000-4000-8000-00000000a003","patientId":"00000000-0000-4000-8000-00000000a005","appoin ...
5. Each practice to itself, each patient to themselves
Section titled “5. Each practice to itself, each patient to themselves”GET /practices/1f235636-8be0-4302-b456-a8796b65cc40/agendaas demo-manager-b:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission scheduling:agenda.read is not granted","requestId":"34468e37-7000-4cf9-99ed-6a34b8c4555c"}GET /practices/1f235636-8be0-4302-b456-a8796b65cc40/occupancyas demo-manager-b:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission analytics:occupancy.read is not granted","requestId":"c233bb5c-9de8-4141-8da6-2b9633bfcf0d"}GET /appointmentsas demo-manager-b:200{"items":[],"total":0}GET /appointments/2424c221-de78-40f1-bce0-1de51371ef14as demo-patient-2:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission scheduling:appointment.read is not granted","requestId":"3ea46516-e89c-4e78-9d9c-c03842a6da68"}GET /calendars/8ec0b2b3-196a-45ba-920f-ab3713e2dd6c/days/2026-10-17as demo-patient-2:200{"calendarId":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c","day":"2026-10-17","timeZone":"Europe/Paris","open":[{"startsAt":"2026-10-16T22:00:00.000Z","endsAt":"2026-10-17T21:30:00.000Z"}],"timeOff":[],"appointmentTypes":[{"name":"Consultation","duration":30,"grid":30,"buffer":0,"needsConfirmation":false,"id":"a5be6ca2-d996-4737-a92c-99fafb1d481c"}],"busy":[{"startsAt":"2026-10-17T06:00:00.000Z","endsAt":"2026-10-17T06:30:00.000Z","heldUntil":"2026-10-17T06:30:00.000Z"},{"startsAt":"2026-10-17T07:00:00.000Z","endsAt":"2026-10-17T07:30:00.000Z","heldUntil":"2026-10-17T07:30:00.000Z","appointment":{"id":"aed94000-37e1-4d5f-9072-d4e99845d07a","patientId":"00000000-0000-4000-8000-00000000a006","appoin ...GET /practices/1f235636-8be0-4302-b456-a8796b65cc40/agendaas demo-manager:200{"items":[{"id":"2424c221-de78-40f1-bce0-1de51371ef14","calendarId":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c","practiceId":"1f235636-8be0-4302-b456-a8796b65cc40","practitionerId":"00000000-0000-4000-8000-00000000a003","patientId":"00000000-0000-4000-8000-00000000a005","appointmentTypeId":"a5be6ca2-d996-4737-a92c-99fafb1d481c","startsAt":"2026-10-17T06:00:00.000Z","endsAt":"2026-10-17T06:30:00.000Z","day":"2026-10-17","status":"booked"},{"id":"aed94000-37e1-4d5f-9072-d4e99845d07a","calendarId":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c","practiceId":"1f235636-8be0-4302-b456-a8796b65cc40","practitionerId":"00000000-0000-4000-8000-00000000a003","patientId":"00000000-0000-4000-8000-00000000a006","appoin ...
6. Less than 24 hours before: the patient may not cancel any more, the practitioner may, with a reason
Section titled “6. Less than 24 hours before: the patient may not cancel any more, the practitioner may, with a reason”POST /appointmentsas demo-practitioner:201{"id":"d87e58d3-ee96-401e-8b9e-787b91e8bb83"}POST /appointments/d87e58d3-ee96-401e-8b9e-787b91e8bb83/cancellationas demo-patient:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission scheduling:appointment.cancel is not granted","requestId":"d3392e6d-8b21-4ca5-923b-f6fa65675b5d"}POST /appointments/d87e58d3-ee96-401e-8b9e-787b91e8bb83/cancellationas demo-practitioner:422{"type":"urn:problem:reason-required","title":"Unprocessable Content","status":422,"code":"REASON_REQUIRED","detail":"The practice gives a reason when it cancels an appointment.","requestId":"907b8cfd-7f50-43de-ad3c-00152ebf811b"}POST /appointments/d87e58d3-ee96-401e-8b9e-787b91e8bb83/cancellationas demo-practitioner:201{"id":"d87e58d3-ee96-401e-8b9e-787b91e8bb83"}
7. The other contexts followed: notifications sent, figures updated
Section titled “7. The other contexts followed: notifications sent, figures updated”GET /notificationsas demo-patient:200{"items":[{"id":"b7c55a31-eb0c-4ce6-8f5a-e116ccc04fdb","kind":"confirmation","appointmentId":"d87e58d3-ee96-401e-8b9e-787b91e8bb83","startsAt":"2026-10-10T09:00:00.000Z","sentAt":"2026-10-10T05:45:20.116Z"},{"id":"6bbb4bdc-fdf3-4bd1-a5a1-50025391810c","kind":"confirmation","appointmentId":"2424c221-de78-40f1-bce0-1de51371ef14","startsAt":"2026-10-17T06:00:00.000Z","sentAt":"2026-10-10T05:45:20.005Z"},{"id":"2d40af75-5210-4746-9d01-b899e1a70600","kind":"cancellation","appointmentId":"809c9b4b-9d96-4213-90f2-05ec884b0d8e","startsAt":"2026-10-10T09:00:00.000Z","sentAt":"2026-10-10T05:41:11.263Z"},{"id":"41b1ae41-2298-437a-9b42-ec8892790706","kind":"confirmation","appointmentId":"809c9b4b-9d96-4 ...GET /practices/1f235636-8be0-4302-b456-a8796b65cc40/occupancyas demo-manager:200{"items":[{"id":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c.2026-10-10","calendarId":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c","day":"2026-10-10","heldMinutes":0,"appointmentsHeld":0,"completed":0,"noShows":0,"cancelledByPatient":0,"cancelledByPractice":1},{"id":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c.2026-10-17","calendarId":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c","day":"2026-10-17","heldMinutes":60,"appointmentsHeld":2,"completed":0,"noShows":0,"cancelledByPatient":0,"cancelledByPractice":0}],"total":2}
8. Support looks on behalf of the patient, and cannot write
Section titled “8. Support looks on behalf of the patient, and cannot write”demo-support impersonates demo-patient in Keycloak: the token is the patient's, with impersonator_id = 00000000-0000-4000-8000-00000000a008.
GET /appointments/2424c221-de78-40f1-bce0-1de51371ef14as support-as-patient:200{"id":"2424c221-de78-40f1-bce0-1de51371ef14","calendarId":"8ec0b2b3-196a-45ba-920f-ab3713e2dd6c","practiceId":"1f235636-8be0-4302-b456-a8796b65cc40","practitionerId":"00000000-0000-4000-8000-00000000a003","patientId":"00000000-0000-4000-8000-00000000a005","appointmentTypeId":"a5be6ca2-d996-4737-a92c-99fafb1d481c","startsAt":"2026-10-17T06:00:00.000Z","endsAt":"2026-10-17T06:30:00.000Z","day":"2026-10-17","status":"booked","patientName":"Pia Patient"}POST /appointments/2424c221-de78-40f1-bce0-1de51371ef14/cancellationas support-as-patient:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission CancelAppointmentCommand is not granted","requestId":"2e43c8a7-e6d9-4f7e-a3ce-89ace1ef81c2"}
9. The audit trail: who was allowed, who was refused, who looked on whose behalf
Section titled “9. The audit trail: who was allowed, who was refused, who looked on whose behalf”GET /audit/entriesas demo-manager:403{"type":"urn:problem:forbidden","title":"Forbidden","status":403,"code":"FORBIDDEN","detail":"Permission audit:trail.read is not granted","requestId":"b79657dd-a153-48be-b949-db2346ea602b"}GET /audit/entries?outcome=denied&actorId=00000000-0000-4000-8000-00000000a007&limit=3as demo-admin:200{"items":[{"id":"0afb7e92-294c-4896-aaef-3e56795e56bd","chain":"global","sequence":68,"occurredAt":"2026-10-10T05:41:11.046Z","category":"authorization","action":"scheduling:agenda.read","outcome":"denied","reason":"NO_RELATION","actorId":"00000000-0000-4000-8000-00000000a007","actorType":"user","resourceType":"practice","resourceId":"20026e9e-ba63-4cbf-b8d3-bd2d4a1084bf","context":"scheduling","correlationId":"e93c2b8e-3105-49be-8c72-f1a011713593","origin":"http","previousHash":"ecb1118f2798ce94b0b59466b104a36cd25f609618e7154011832a51d918a703","hash":"5e078d3bf225882a4bb76ebcc58d46b475eae9d270640a0b4b3bba40dea7c623"},{"id":"71615a41-c1ea-400b-b3db-30ced1b8c86f","chain":"global","sequence":6 ...GET /audit/entries?actorId=00000000-0000-4000-8000-00000000a005&limit=100as demo-admin:200{"items":[{"id":"51d69071-8347-481c-b48c-86a5ae31b7d8","chain":"global","sequence":15,"occurredAt":"2026-10-10T05:41:10.624Z","category":"authorization","action":"identity:person.register","outcome":"allowed","actorId":"00000000-0000-4000-8000-00000000a005","actorType":"user","resourceType":"context","resourceId":"identity","context":"identity","correlationId":"4f0710f7-62f5-494d-8ff0-142fb5c05691","origin":"http","previousHash":"c0b977e893115d2f4892c5260e1e666fbedb13aac4d5cb1f273a4a05c48d7549","hash":"6f2139c565824aed43fbc5757b08189381e8214351120084c44ce37d57b6f880"},{"id":"f1e64721-3f03-4f59-90d6-b588cda446b7","chain":"global","sequence":17,"occurredAt":"2026-10-10T05:41:10.636Z","category ...Entries of the patient that carry the impersonator 00000000-0000-4000-8000-00000000a008: 4, for example:
{ "id": "b0d4eadc-34b6-44e0-9e07-74a1c73cf5a7", "chain": "global", "sequence": 92, "occurredAt": "2026-10-10T05:41:11.619Z", "category": "authorization", "action": "scheduling:appointment.cancel", "outcome": "allowed", "actorId": "00000000-0000-4000-8000-00000000a005", "actorType": "user", "impersonatedBy": "00000000-0000-4000-8000-00000000a008", "resourceType": "appointment", "resourceId": "40485b8b-4445-44d2-b183-389032bad003", "context": "scheduling", "correlationId": "f58c75ea-0802-4b87-ad0c-599396d42968", "origin": "http", "previousHash": "73e5cc0c5b9deca21cc0b3690e01fc2f8bb92595f63228da0ce79246633c498f", "hash": "eb10f50ae57950e8617a6c82ca4a9996f128deea189424eef1cf1520775f54d7"}10. One booking, one trace, read back from Jaeger
Section titled “10. One booking, one trace, read back from Jaeger”GET http://127.0.0.1:54686/api/v3/traces/144a053238455edb659a6422e6e1e5a1: 107 spans in one trace, from the processes scheduling (the API and the worker are one consumer group: either may take a delivery). Distinct names:
pg.query:BEGIN apppg.query:INSERT apppg.query:SELECT apppg.query:UPDATE apppg.query:COMMIT apprepository CalendarRepository.findByIdrepository AppointmentRepository.heldOnrepository AppointmentRepository.heldByrepository AppointmentRepository.insertexternal SchedulingRelationships.recordAppointmenthandler AppointmentBookedEventHandleroutbox append scheduling.appointment.bookedhandler AppointmentBookedPublisherrepository Agenda.writtenrepository Agenda.upsertIfNewerdomain-event scheduling.appointment.bookedrepository AppointmentRepository.savecommand BookAppointmentCommandpublish scheduling.appointment.bookedrepository OutcomeRegister.writtenrepository OutcomeRegister.upsertIfNewerprocess analytics.outcomesrepository NotificationRepository.findByCauserepository Contacts.getexternal Mailer.transportexternal Mailer.sendexternal Mailer.deliverrepository OutcomeRegister.getrepository NotificationRepository.insertrepository OutcomeRegister.listrepository OccupancyRegister.writtenrepository OccupancyRegister.upsertIfNewerrepository ReminderRepository.findByAppointmentrepository ReminderRepository.insertrepository ReminderRepository.saveprocess analytics.occupancycommand PlanReminderCommandhandler NotificationSentEventHandlerdomain-event notification.notification.sentrepository NotificationRepository.savecommand SendNotificationCommandprocess notification.scheduling-appointment-bookedpg.query:SAVEPOINT apppg.query:RELEASE appPOST /appointments11. An unexpected failure reaches the error tracker with the trace id of its request
Section titled “11. An unexpected failure reaches the error tracker with the trace id of its request”GET /notificationsas demo-patient:500{"type":"about:blank","title":"Internal Server Error","status":500,"code":"INTERNAL_ERROR","requestId":"942c262e-f314-4213-9983-45b2c3951640"}GlitchTip holds 1 event of the trace 5e830714a9088cc4c7e91b5aa53d9e70:
{ "type": "error", "traceId": "5e830714a9088cc4c7e91b5aa53d9e70", "operation": "ListNotificationsQuery", "origin": "http", "errorCode": "42P01", "correlationId": "942c262e-f314-4213-9983-45b2c3951640", "actorId": "00000000-0000-4000-8000-00000000a005"}GET /notifications?limit=1as demo-patient:200{"items":[{"id":"1c4c0cf1-f511-410d-a07f-b047152a4fdc","kind":"cancellation","appointmentId":"d87e58d3-ee96-401e-8b9e-787b91e8bb83","startsAt":"2026-10-10T09:00:00.000Z","sentAt":"2026-10-10T05:45:20.227Z"}],"total":6}
Every step answered as designed.
Scheduled jobs ran in the worker, not in the API
Section titled “Scheduled jobs ran in the worker, not in the API”The worker's log at the first five-minute boundary (the API's log holds no line of origin
schedule):
{"level":"info","time":"2026-10-10T05:45:00.051Z","service":"scheduling-worker","environment":"development","job":"scheduling.expire-unconfirmed-appointments","context":"scheduling","outcome":"succeeded","scheduledAt":"2026-10-10T05:45:00.000Z","durationMs":43,"correlationId":"3fca9e9a-dbf2-44f8-8e8e-d8a4fff8e3ee","message":"Sch{"level":"info","time":"2026-10-10T05:45:00.057Z","service":"scheduling-worker","environment":"development","job":"identity.expire-delegations","context":"identity","outcome":"succeeded","scheduledAt":"2026-10-10T05:45:00.000Z","durationMs":53,"correlationId":"2084920b-1393-4519-b9f2-c6ca9294d3eb","message":"Scheduled job succee{"level":"info","time":"2026-10-10T05:45:00.063Z","service":"scheduling-worker","environment":"development","job":"notification.send-due-reminders","context":"notification","outcome":"succeeded","scheduledAt":"2026-10-10T05:45:00.000Z","durationMs":56,"correlationId":"76c42bc3-52aa-4108-805d-1bc72359a913","message":"Scheduled joWhat the run showed that the tests had not
Section titled “What the run showed that the tests had not”- Read models written twice on one key failed on the real clock until the workaround of
src/shared-kernel/storable-stamp.ts(a defect of@ddd-ts-kit/knex, seedocs/tooling-report.md). - A refusal by a policy answers
403 FORBIDDENnaming the permission; the policy's own reason code does not reach the HTTP body (the kit's problem format has no member for it). - The decision recorded for the impersonated cancellation is
allowed(the patient may cancel their appointment); the refusal that follows comes from the read-only rule of impersonated sessions and is recorded as the failed command. - Consumers run in both processes as one consumer group: a delivery is taken by the API or by the worker, in the same trace either way.
Stopping
Section titled “Stopping”kill %1 %2docker compose --profile identity --profile observability --profile errors down --volumesRun again after analytics moved to read time
Section titled “Run again after analytics moved to read time”A second real run of scripts/demo.mjs, on 2026-10-10, after the stored occupancy was removed:
the same commands on an empty database, with the profiles identity and observability only
(no GlitchTip, so step 11 answered "SENTRY_DSN is not set: skipped."). Every other step answered
as designed. Two steps differ in what serves them, not in what a client reads.
Step 7, the occupancy, now one grouped statement over the outcomes. The same shape and the same figures as above for the same scenario (the identifiers are those of this run):
GET /practices/f6fe6079-0b05-4855-a9e3-ae518ed15a45/occupancyas demo-manager:200{"items":[{"id":"767d0ef7-879d-400b-bc38-f0aa4ef4f3a5.2026-10-10","calendarId":"767d0ef7-879d-400b-bc38-f0aa4ef4f3a5","day":"2026-10-10","heldMinutes":0,"appointmentsHeld":0,"completed":0,"noShows":0,"cancelledByPatient":0,"cancelledByPractice":1},{"id":"767d0ef7-879d-400b-bc38-f0aa4ef4f3a5.2026-10-17","calendarId":"767d0ef7-879d-400b-bc38-f0aa4ef4f3a5","day":"2026-10-17","heldMinutes":60,"appointmentsHeld":2,"completed":0,"noShows":0,"cancelledByPatient":0,"cancelledByPractice":0}],"total":2}
Step 10, the trace of the booking. analytics is one consumer, which writes the outcome; there
is no process analytics.occupancy, no OutcomeRegister.get or .list and no
OccupancyRegister any more:
GET http://127.0.0.1:54686/api/v3/traces/bf1d9dd666a392d44302b8d55771b7db: 92 spans in one trace, from the processes scheduling (the API and the worker are one consumer group: either may take a delivery). Distinct names:
pg.query:BEGIN apppg.query:INSERT apppg.query:SELECT apppg.query:UPDATE apppg.query:COMMIT apppg.query:SAVEPOINT apppg.query:RELEASE apprepository CalendarRepository.findByIdrepository AppointmentRepository.heldOnrepository AppointmentRepository.heldByrepository AppointmentRepository.insertexternal SchedulingRelationships.recordAppointmenthandler AppointmentBookedEventHandleroutbox append scheduling.appointment.bookedhandler AppointmentBookedPublisherrepository Agenda.writtenrepository Agenda.upsertIfNewerdomain-event scheduling.appointment.bookedrepository AppointmentRepository.savecommand BookAppointmentCommandpublish scheduling.appointment.bookedrepository NotificationRepository.findByCauserepository Contacts.getexternal Mailer.transportexternal Mailer.deliverrepository OutcomeRegister.writtenrepository NotificationRepository.insertprocess analytics.outcomesrepository ReminderRepository.findByAppointmentrepository ReminderRepository.insertrepository ReminderRepository.savecommand PlanReminderCommandhandler NotificationSentEventHandlerdomain-event notification.notification.sentrepository NotificationRepository.savecommand SendNotificationCommandprocess notification.scheduling-appointment-bookedPOST /appointments