Add the Events cluster Specs - #718
Conversation
Moves Event, Booking, Location, Capacity, RSVP Status, Confirm Booking and Check In Booking into cards/events as Specs with their example instances. Bookings link the two Event instances and the CRM contacts already in the catalog; Location reads the catalog's geo-point field and Capacity the State Pill hue helpers. Booking's isolated component is a module-level class so its tracked state type-checks, and its action handlers return void. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Staging Submissions PreviewThis PR's content is pushed to the staging submissions realm: https://realms-staging.stack.cards/submissions/ Changed folders:
Updated at 2026-09-11 10:20:11 UTC for commit |
richardhjtan
left a comment
There was a problem hiding this comment.
[Claude Code 🤖] Reviewed as a move: what the modules and instances lost between the source realm and here, plus how the seven Specs land in the surface this cluster exists to appear in. I did not render anything.
The code is sound; the example set isn't. Two blank Bookings, two instances whose filenames no longer describe their contents, and check-in timestamps nine months ahead of their event.
- Two all-null Bookings came across with the move — thread on
Booking/76353cf8-…json. Location/northgate-fulfillment-centre.jsonholds a training ground, costing the Location Spec its non-event example — thread there.- Check-in and reference dates contradict their events — thread on
Booking/bk-20260802-00101.json. Event/northbridge-v-riverton.jsonholds an awards night — thread there.- Seven Specs ship with no
cardDescription, andcardTitlediverges from the CRM cluster's convention — thread onSpec/event.json. - The Booking docblock now documents the isolated component — thread in
booking.gts. - Confirm is gated behind check-in eligibility — thread in
booking.gts.
Adjacent, not asked of this PR: get places() is written out three times in booking.gts, and Segment.ring in capacity-field.gts is assigned and never read — both inherited from the source module.
| "data": { | ||
| "type": "card", | ||
| "attributes": { | ||
| "reference": null, |
There was a problem hiding this comment.
[Claude Code 🤖] This booking and ad4c7635-3cf8-4a5d-b06c-dec6756b058f.json are blank — every attribute null, no holder, and this one has no event link either. They came across with the move and now sit in the catalog beside the seven authored examples, rendering as Untitled Booking wherever Bookings are listed or searched. Delete both, or make them the two states the set is missing (a Waitlisted unpaid claim, a Refunded one). Blocking: the example set is the deliverable here.
| "data": { | ||
| "type": "card", | ||
| "attributes": { | ||
| "name": "Northbridge Training Ground", |
There was a problem hiding this comment.
[Claude Code 🤖] The card in northgate-fulfillment-centre.json is a football training ground, so both Location examples are now Northbridge sports venues. The Spec's readMe rests its case on one Location serving “an event's venue and an order's pickup point” — with the fulfilment centre gone nothing demonstrates the second half, and linkedExamples.1 points here. Restore the warehouse content or rename the file. Regression from the move; blocking for the Location Spec.
| "data": { | ||
| "type": "card", | ||
| "attributes": { | ||
| "title": "End of Season Awards Night", |
There was a problem hiding this comment.
[Claude Code 🤖] Filename says a fixture between two clubs, content is an awards dinner — and this is the Event Spec's linked example, so it is what a Spec reader lands on first. Rename the file to match the content, or restore the match. Regression from the move, non-blocking.
| "code": "GBP" | ||
| } | ||
| }, | ||
| "checkedInAt": "2026-08-14T06:40:24.114Z", |
There was a problem hiding this comment.
[Claude Code 🤖] Checked in 2026-08-14 here and 2026-08-02 in bk-20260802-00144, against an event that starts 2027-05-22 — attendance recorded nine months before the doors open. bk-20261101-00602 fails the other way: a 2026-11-01 reference against an event that ended 2026-08-27. checkedInAt is the fact the Check In command exists to write, and the reason the Booking Spec picks a not-yet-checked-in example, so the demonstration set is contradicting the block it demonstrates. Repointing the bookings onto the two Events left their dates behind — re-date one side or the other. Blocking.
| "data": { | ||
| "type": "card", | ||
| "attributes": { | ||
| "cardTitle": "Event card", |
There was a problem hiding this comment.
[Claude Code 🤖] All seven Specs leave cardDescription empty. Spec's own embedded and fitted pass it straight through as the secondary line (@secondary={{@model.cardDescription}} in packages/base/spec.gts), so every hit for this cluster in Spec search shows a title over a blank line — the surface this change exists to populate. fields/status/Spec and cards/crm/Spec/pipeline-stage.json both fill it. Separately, cardTitle here appends the kind (“Event card”, “Capacity field”) where the CRM cluster leaves cardTitle unset and pipeline-stage uses the bare name — worth settling before the next cluster copies whichever it happens to see. Non-blocking.
| /** | ||
| * A claim on places at an Event — who is coming, how many places, where the | ||
| * money stands, and whether they actually showed up. Four facts, four | ||
| * fields, deliberately not collapsed: `rsvp` is intent, `paymentStatus` is | ||
| * money, `checkedInAt` is what happened, `quantity` is how much of the | ||
| * capacity this claim consumes. | ||
| * | ||
| * `checkedInAt` is an event fact written once by the Check In Booking | ||
| * command (which is also what makes a ticket one-time-use); anything | ||
| * derived — attendance rates, no-show lists — is computed from it, never | ||
| * stored. Seat-level assignment (section, row, seat) is a seating-plan | ||
| * concern for the consumer's extending card. | ||
| */ |
There was a problem hiding this comment.
[Claude Code 🤖] Hoisting the isolated component left this docblock — the Booking block's own four-facts documentation — attached to BookingIsolated; export class Booking below has none. It now reads as documentation of a component, and anything regenerating the Spec readMe from the module will take it that way. Move it back onto the card class. Non-blocking.
| {{#if this.canCheckIn}} | ||
| <div class='actions'> | ||
| {{#if this.canConfirm}} |
There was a problem hiding this comment.
[Claude Code 🤖] Confirm sits inside {{#if this.canCheckIn}}, so it inherits check-in's gate: canConfirm's own rsvp !== 'Declined' clause can never decide anything, and a Declined booking has no route back to Going from the card — though the RSVP graph allows Declined → Going and ConfirmBookingCommand will perform it. Deliberate? If so, drop the dead clause; if not, lift Confirm out to its own {{#if this.canConfirm}}. Non-blocking.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
First step-2 cluster. Moves Events from the matrix layer into
cards/eventsas Specs, so the blocks are found by Spec search:event,booking,location,capacity-field,rsvp-status-field,confirm-booking,check-in-bookingwith their seven Specs. Booking holders are the CRM contacts already incards/crm; RSVP Status and the lifecycle fields build on the catalog Status field; Location readsfields/geo-point; Capacity takes its hues fromcomponents/state-pill.adoptsFromand link refs are relative.booking.gts: the isolated component is hoisted to a module-level class so its@trackedstate type-checks, the Confirm and Check in handlers return void, and the lazy command imports carry a typed expect-error because the catalog type-check wants an extension the realm loader does not use.Lint and type-check clean on the contents mirror; render verification on staging after merge, as with the other batches.
🤖 Generated with Claude Code