{
  "schema": "link.gameday.stages/1",
  "title": "Game Day on Link",
  "subtitle": "How the chapter's platform carries Army/Navy from the first ticket to the report to national.",
  "doctrine": "Live means running on the chapter site today. Modelled means a framework: what it does, who sees what, what it reads and writes, what it reuses, what ships it and how we know it is done. A framework is built when development is ripe; until then it is the build seed, never shown as live. Written 2026-10-12 for the board of 13 Oct (WO-2026-1012-LAOC-READINESS-001).",
  "event": {
    "id": "evt_LAOC_ARMYNAVY_20261212",
    "org": "laoc",
    "name": "Army/Navy 2026 Watch Party aboard Battleship IOWA",
    "date": "2026-12-12",
    "doors": "11:00",
    "colors": "11:45",
    "kickoff": "12:00",
    "close": "16:00",
    "capacity": 300,
    "deck_seated": 280,
    "floor_tickets": 200,
    "api": "https://api.laoc.link.onamerica.org"
  },
  "status_vocabulary": {
    "live": "running on the chapter site today",
    "modelled": "a framework and a build seed; built when development is ripe",
    "next": "queued behind a decision or a dependency named in the framework"
  },
  "stages": [
    {
      "id": "register",
      "n": 1,
      "name": "Register",
      "when": "From 1 November, once the board sets the price",
      "member": "Picks a seat zone, says who they are (Navy, West Point, Air Force, guest), chooses an entrée per ticket, pays by card, and has a confirmation before the page closes.",
      "staff": "Watches tickets climb against the 200-ticket floor and the 280-seat deck, sees entrée tallies forming for the caterer, and never touches a spreadsheet.",
      "capabilities": [
        {
          "name": "Event registration",
          "status": "live",
          "surface": "/register/?event=evt_LAOC_ARMYNAVY_20261212",
          "what": "Org-fenced registration: name, email, mobile, affiliation. The flyer's QR and the myUSNA post both point here. One verification record from September sits in it today."
        },
        {
          "name": "Ticket purchase with entrée and seat zone",
          "status": "modelled",
          "framework": {
            "purpose": "Sell the seat and capture the two facts game day needs from every guest: which entrée, which zone.",
            "member_sees": "Three zones at their prices (front row, main deck, stern), an entrée choice per ticket, card checkout with the option to cover the processing fee, a receipt.",
            "staff_sees": "Tickets sold against the floor and the deck, entrée tallies, revenue against budget, all live.",
            "reads": ["event", "zones and prices (pricing config)", "seat units"],
            "writes": ["participant", "order and payment ledger row, integer cents, rail = Stripe", "entrée", "zone"],
            "reuses": ["modules/orgs.js org fence and registration route", "lib/donations.js integer-cents ledger", "lib/sponsors.js tiers as data", "Stripe signature check and webhook settle (workers/reunion-tchs2006)"],
            "ships_with": ["WO-2026-0917-LAOC-PRICING-001 tasks 2-6: store, repositories, EventModel, pricing routes, payments ledger, Stripe checkout fail-closed", "The chapter's Stripe account (Treasurer's go-ahead 27 Sep; built after the board approves the package)"],
            "done_when": "A real card buys a real seat; the ledger shows it in cents; the caterer tally moves by one; and with Stripe absent nothing is sold at all."
          }
        },
        {
          "name": "Reserve a whole table",
          "status": "modelled",
          "framework": {
            "purpose": "Let a class, a company or a family take a table of ten at a premium, front row first.",
            "member_sees": "A table in the deck plan, its price, and ten names to fill in later.",
            "staff_sees": "The table marked reserved in the studio and the console.",
            "reads": ["seat units", "zone prices"],
            "writes": ["a party holding ten seats", "one order"],
            "reuses": ["lib/pairing_roster.js party queue and read-back rule", "the studio's saved layout"],
            "ships_with": ["WO-2026-0917-LAOC-PRICING-001 task 4: units, seats, placement"],
            "done_when": "A reserved table shows reserved in the studio within a second of purchase."
          }
        }
      ]
    },
    {
      "id": "confirm",
      "n": 2,
      "name": "Confirm",
      "when": "Minutes after registering; reminders a week out and the day before",
      "member": "Gets a confirmation from laoc@link.onamerica.org with a QR code and a check-in link, can change their RSVP, and receives a know-before-you-go the day before: parking, doors at 11:00, colors at 11:45.",
      "staff": "Sees every send and open in the delivery ledger; roster watchers are told of new registrations as they happen.",
      "capabilities": [
        {
          "name": "Branded confirmation and magic link",
          "status": "live",
          "surface": "/staff/",
          "what": "Every member-facing email carries the chapter's name and sender (modules/brand.js), delivered through Postmark with a delivery ledger. The magic link signs a member in without a password."
        },
        {
          "name": "RSVP changes",
          "status": "live",
          "surface": "/rsvp/",
          "what": "A guest changes their answer from the link in their email; the roster updates."
        },
        {
          "name": "Receipt, count-freeze notice and know-before-you-go",
          "status": "modelled",
          "framework": {
            "purpose": "Tell each guest what they bought and what to expect, on a timeline the event owns.",
            "member_sees": "A receipt with entrée and zone; a note a week out that counts freeze; a day-before guide with parking, the gate, the program.",
            "staff_sees": "The timeline, what has gone out, who opened it.",
            "reads": ["order", "event timeline settings"],
            "writes": ["comms ledger entries"],
            "reuses": ["comms module scheduled sends (the console's message scheduler, L2g-7)", "IOWA's 2025 know-before-you-go as the content precedent"],
            "ships_with": ["the ticketing slice above", "a per-event timeline setting"],
            "done_when": "Every ticket holder receives both notes, the ledger shows delivery, and no email carries anyone else's data."
          }
        }
      ]
    },
    {
      "id": "arrive",
      "n": 3,
      "name": "Arrive",
      "when": "11:00 to 12:00, starboard side",
      "member": "Shows the QR code or types the six-digit code, or registers at the table as a walk-up, and is told their table by name: Alpha through Yankee.",
      "staff": "Sees registered, checked in and awaiting counts move live; registers walk-ups in one motion; reads the affiliation chip for the name tag.",
      "capabilities": [
        {
          "name": "QR and code check-in",
          "status": "live",
          "surface": "/checkin/",
          "what": "Check in by the link in the email, the QR, or a typed code. The code is paired to its event token, so a link opens only for its own holder."
        },
        {
          "name": "Walk-up register and check-in",
          "status": "live",
          "surface": "/console/",
          "what": "A staff member registers and checks in a walk-up in one form; the attendance report counts them."
        },
        {
          "name": "Table assignment at the door and the name tag",
          "status": "modelled",
          "framework": {
            "purpose": "Turn the studio's modelled seats into actual seats as guests arrive, so nobody wanders the deck with a plate.",
            "member_sees": "Their table name on the check-in screen and in their portal.",
            "staff_sees": "An unseated queue for walk-ups and a deck view filling in.",
            "reads": ["seat units and placements", "check-in records"],
            "writes": ["check-in record with seat", "placement"],
            "reuses": ["lib/pairing_roster.js read-back rule and unassigned queue", "studio/plans/iowa-ship.js deck renderer"],
            "ships_with": ["WO-2026-0917-LAOC-PRICING-001 task 4: units, seats, placement", "the studio's saved layout as the source of truth"],
            "done_when": "A checked-in guest's table shows in their portal and in the console within a second, and the studio's seats read actual, not modelled."
          }
        }
      ]
    },
    {
      "id": "ondeck",
      "n": 4,
      "name": "On deck",
      "when": "11:00 to 16:00",
      "member": "Opens their event page: the program (colors 11:45, kickoff 12:00, raffle at halftime), their table, their entrée, where the bar and the heads are, a button to text the desk, and the photo album as it fills.",
      "staff": "Sees the same page from the other side; every request for help lands in one queue.",
      "capabilities": [
        {
          "name": "My Event portal",
          "status": "live",
          "surface": "/portal/",
          "what": "The guest's own page: schedule, check-in state, and the standing line 'Questions? Ask any chapter staff member.'"
        },
        {
          "name": "Photo album",
          "status": "live",
          "surface": "/photo/",
          "what": "Photos from the day, uploaded by staff, visible to guests."
        },
        {
          "name": "Program timeline and deck card",
          "status": "modelled",
          "framework": {
            "purpose": "Put the day's program and the guest's own table on the deck plan in their hand.",
            "member_sees": "A timeline for the day and the deck with their table lit.",
            "staff_sees": "The same, with every table's state.",
            "reads": ["event timeline settings", "the studio's layout", "the guest's seat"],
            "writes": [],
            "reuses": ["studio/plans/iowa-ship.js and the venue fixtures", "My Event portal"],
            "ships_with": ["WO-2026-0917-LAOC-PRICING-001 task 4", "a per-event timeline setting"],
            "done_when": "The portal shows the deck with the guest's table lit on a phone, signed out, in under two seconds."
          }
        },
        {
          "name": "Support desk",
          "status": "modelled",
          "framework": {
            "purpose": "One number and one inbox for day-of help: a lost ticket, a dietary question, accessibility, parking.",
            "member_sees": "A 'Text the desk' button that opens a message to the chapter's number.",
            "staff_sees": "A queue with who, where and what, and a resolve button; every resolution attributed to the staff member who did it.",
            "reads": ["participant", "check-in record"],
            "writes": ["support ticket", "ops ledger entry, actor attributed"],
            "reuses": ["SMS outbox and Text Desk (comms)", "SPEC-2026-1002-LINK-OPSMATRIX-001 ops ledger"],
            "ships_with": ["SPEC-2026-1002-LINK-OPSMATRIX-001 first increment"],
            "done_when": "A guest's text appears in the console within seconds and its resolution is on the ledger with a name and a time."
          }
        },
        {
          "name": "Raffle",
          "status": "modelled",
          "framework": {
            "purpose": "Sell item-raffle tickets and draw the winners the way California's charity raffle law (Penal Code 320.5) requires.",
            "member_sees": "Paper tickets bought in person on the fantail, each with a numbered stub, and the draw at halftime. The portal announces the raffle and its prizes but sells no tickets: a raffle ticket may not be sold over the internet (320.5(f)(2)).",
            "staff_sees": "A ledger of the tickets sold in person, by stub number, cash or card. At the draw, a stub is pulled by hand from the drum holding every stub sold, with an adult supervising (320.5(b)(2) and (b)(3)), and each winner is recorded against the stub number drawn.",
            "reads": ["participants"],
            "writes": ["raffle sales: integer cents, rail = cash or card taken in person, and the stub numbers sold", "draw record: stub drawn, prize, supervisor, time"],
            "reuses": ["the payments ledger for the money. A raffle ticket is a purchase, not a gift, and gets no donation receipt (IRS Pub 526)", "donations record-and-receipt for a donated prize: an in-kind gift, described and never valued"],
            "ships_with": ["the chapter's raffle registration for the year, confirmed in writing by the Attorney General before any ticket is sold; at least 90% of gross receipts to the charitable purpose; the CT-NRP-2 report by 1 February", "the chapter's eligibility: exempt from California tax under R&T 23701d and qualified in California for a year (320.5(c)). Federal 501(c)(3) status alone does not qualify it", "no 50/50: in California it is illegal unless the organization qualifies under Penal Code 320.6 (nonprofits affiliated with major-league teams, or established by the PGA, LPGA or NASCAR), which the chapter does not", "a raffle module after the registration is in hand"],
            "done_when": "Every ticket sold is on the ledger and its stub is in the drum, the ledger's count equals the number of stubs drawn from, and the draw is supervised and recorded."
          }
        }
      ]
    },
    {
      "id": "run",
      "n": 5,
      "name": "Run the day",
      "when": "Staff, 09:00 to 16:00",
      "member": "Never sees this stage, which is the point.",
      "staff": "Signs in by magic link or Face ID; the console shows registered, checked in, awaiting and walk-ups; the caterer's counts are frozen and sent from the same screen; every action is attributed.",
      "capabilities": [
        {
          "name": "Staff sign-in",
          "status": "live",
          "surface": "/staff/",
          "what": "Magic link first, Face ID opt-in after; org-branded email."
        },
        {
          "name": "Arrival and membership console",
          "status": "live",
          "surface": "/console/",
          "what": "Registered, checked in, awaiting, walk-up registration, and the attendance report tab."
        },
        {
          "name": "Caterer count freeze and day-of tally",
          "status": "modelled",
          "framework": {
            "purpose": "Give the caterer exact entrée counts 72 hours out and a live tally on the day.",
            "member_sees": "Nothing; their entrée was chosen at purchase.",
            "staff_sees": "Entrée totals, a 'freeze' action that emails the caterer a signed count sheet, and the day's tally against it.",
            "reads": ["orders and entrées"],
            "writes": ["freeze record", "comms ledger entry"],
            "reuses": ["comms module", "the ticketing slice's entrée field"],
            "ships_with": ["the ticketing slice above"],
            "done_when": "The frozen sheet equals what the caterer invoices."
          }
        },
        {
          "name": "Ops ledger and device registry",
          "status": "modelled",
          "framework": {
            "purpose": "Make the day replayable: who checked whom in, who sold which raffle ticket, from which device.",
            "member_sees": "Nothing.",
            "staff_sees": "Their device enrolled by QR; every action they take recorded under their name.",
            "reads": ["staff sessions", "device registry"],
            "writes": ["ops ledger entries"],
            "reuses": ["SPEC-2026-1002-LINK-OPSMATRIX-001: media classes, device registry, grant matrix, actor-attributed ledger"],
            "ships_with": ["SPEC-2026-1002-LINK-OPSMATRIX-001"],
            "done_when": "The day can be replayed from the ledger alone."
          }
        }
      ]
    },
    {
      "id": "after",
      "n": 6,
      "name": "Afterwards",
      "when": "The week after",
      "member": "Receives a thank-you with the album, a two-question survey, and a receipt for any gift they made, such as a donated prize or a sponsorship. A raffle ticket is a purchase and gets none.",
      "staff": "Exports attendance to national in its own column format, closes the books against the 200-ticket floor, and records the board's decisions in sealed minutes.",
      "capabilities": [
        {
          "name": "Attendance report",
          "status": "live",
          "surface": "/console/",
          "what": "Registered, attended, by affiliation; the console's report tab."
        },
        {
          "name": "Minutes and board room",
          "status": "live",
          "surface": "/minutes/",
          "what": "Decisions drafted, distributed for review, ratified and sealed; the archive is public to members."
        },
        {
          "name": "Export in national's format (myUSNA)",
          "status": "modelled",
          "framework": {
            "purpose": "Report active membership and attendance to national without hand edits.",
            "member_sees": "Nothing.",
            "staff_sees": "One export button producing the file national asks for.",
            "reads": ["attendance report"],
            "writes": ["a CSV or XLSX in national's columns"],
            "reuses": ["the attendance report", "myUSNA interface tier 0 from WO-2026-0908-LINK-USNAAA-LAOC-001"],
            "ships_with": ["chapter services' answer on required columns and cadence"],
            "done_when": "National accepts the file as sent."
          }
        },
        {
          "name": "Financial close against the floor",
          "status": "modelled",
          "framework": {
            "purpose": "Show the board, to the cent, how the event did against the 200-ticket floor.",
            "member_sees": "Nothing.",
            "staff_sees": "Tickets, bar commission and sponsorships against venue, catering, insurance and the raffle prizes; the actual break-even; and, apart from it, the raffle: its gross, its costs (at most 10%) and where the other 90% or more went to the chapter's charitable purposes (Penal Code 320.5(b)(4)), never toward the event's break-even. A close the Treasurer signs.",
            "reads": ["payments ledger, raffle sales included", "vendor invoices", "the pricing engine's cost lines"],
            "writes": ["the close record"],
            "reuses": ["the pricing engine's cost lines and break-even", "lib/donations.js ledger"],
            "ships_with": ["WO-2026-0917-LAOC-PRICING-001 task 5: payments ledger"],
            "done_when": "The Treasurer's bank statement reconciles to the close to the cent."
          }
        },
        {
          "name": "Thank-you and survey",
          "status": "modelled",
          "framework": {
            "purpose": "Close the loop with every guest within 48 hours.",
            "member_sees": "A thank-you with the album link and two questions.",
            "staff_sees": "Answers beside attendance.",
            "reads": ["attendance", "album"],
            "writes": ["comms ledger entries", "survey answers"],
            "reuses": ["comms module", "photo album"],
            "ships_with": ["nothing new beyond a survey table"],
            "done_when": "Sent to every attendee within 48 hours; answers readable in the console."
          }
        },
        {
          "name": "Media sorted by class",
          "status": "modelled",
          "framework": {
            "purpose": "Keep evidence, keepsakes and working material apart, each with its own retention.",
            "member_sees": "Keepsakes in the album.",
            "staff_sees": "Evidence and working material in their own places.",
            "reads": ["uploads"],
            "writes": ["media class per item"],
            "reuses": ["SPEC-2026-1002-LINK-OPSMATRIX-001 media classes"],
            "ships_with": ["SPEC-2026-1002-LINK-OPSMATRIX-001"],
            "done_when": "No working file appears in the album and no keepsake is deleted as working material."
          }
        }
      ]
    }
  ]
}
