Skip to content

Enhancement: Digital Waivers (WIP)#461

Draft
baltinerdist wants to merge 66 commits into
amtgard:masterfrom
baltinerdist:feature/digital-waivers
Draft

Enhancement: Digital Waivers (WIP)#461
baltinerdist wants to merge 66 commits into
amtgard:masterfrom
baltinerdist:feature/digital-waivers

Conversation

@baltinerdist

Copy link
Copy Markdown
Contributor

Summary

End-to-end digital waivers for ORK3:

  • Kingdom-admin Waiver Builder — live-preview markdown editor for Kingdom-level (event) and Park-level (park day) waivers, with a per-scope enable toggle. Each save publishes a new version; prior versions are preserved so existing signatures always reference the exact text the player signed.
  • Player Submission Form — pre-filled from the logged-in player's profile, with drawn (finger / mouse / stylus via canvas) or typed (cursive) signatures and an optional minor-representative block.
  • Officer Verification — paginated queue (pending / verified / rejected / stale / all) plus a single-waiver review page where the officer signs off (printed name, persona, office title, signature, date, optional notes). Verifier's own signature uses the same widget.
  • Printable view — clean browser-print page linked from player, officer, and queue surfaces.

Architecture

Standard ORK3 three-layer:

  • system/lib/ork3/class.Waiver.php — domain / yapo DB access (77 unit tests)
  • orkui/model/model.Waiver.php — thin __call pass-through via APIModel
  • orkui/controller/controller.Waiver.php (page) + controller.WaiverAjax.php (JSON)
  • orkui/template/revised-frontend/Waiver_{builder,sign,queue,review,print}.tpl + shared Waiver_signature_widget.{inc.php,css.inc,js.inc}

Uses existing Parsedown (safe mode) for markdown. Signatures stored as normalised JSON stroke arrays (drawn) or plain text (typed); no blob storage, no new dependencies.

DB migration

db-migrations/2026-04-17-digital-waivers.sql — two new tables:

  • ork_waiver_template (versioned, per-kingdom, per-scope)
  • ork_waiver_signature (snapshotted player fields + signature + officer verification)

The legacy ork_mundane.waivered flag and existing file-upload waiver path are untouched.

Integration hooks

  • Kingdomnew profile — two new admin-menu entries: Edit Waivers and Waiver Review Queue
  • Parknew profileWaiver Review Queue for park officers, plus a Sign Park Waiver CTA card for logged-in players when the park's active waiver is enabled
  • Playernew sidebarDigital Waivers card on the player's own profile with per-scope sign/view links

Test plan

  • Unit/domain tests pass: ./tests/php/run-waiver-tests.sh77 passed, 0 failed
  • Manual browser QA (see §5.1 of docs/superpowers/plans/2026-04-17-digital-waivers.md for the full checklist): build → sign → queue → verify → print round-trip in Chrome
  • Verify the Parsedown-rendered markdown displays correctly with heading resets (no gray-box artifacts from global orkui.css)
  • Drawn signature round-trip on mouse + touch + stylus
  • Typed-signature cursive font loads (Homemade Apple via Google Fonts)
  • Minor-representative block collapses/expands correctly
  • Officer verification form prefills from the officer's own mundane record
  • Rejected waiver requires notes; reject without notes surfaces an inline error
  • Re-signing the same active template supersedes the prior pending signature

Notes

  • Spec at docs/superpowers/specs/2026-04-17-digital-waivers-design.md; plan at docs/superpowers/plans/2026-04-17-digital-waivers.md (both on this branch).
  • PR was delivered via the brainstorming → writing-plans → subagent-driven-development superpowers pipeline.

🤖 Generated with Claude Code

@baltinerdist baltinerdist changed the title Enhancement: Digital Waivers Enhancement: Digital Waivers (WIP) Apr 19, 2026
@baltinerdist baltinerdist marked this pull request as draft April 19, 2026 19:59
@baltinerdist baltinerdist force-pushed the feature/digital-waivers branch from 0d7792d to ecf121d Compare April 19, 2026 20:00
@baltinerdist baltinerdist force-pushed the feature/digital-waivers branch from ecf121d to 00cced2 Compare May 11, 2026 16:51
@baltinerdist baltinerdist force-pushed the feature/digital-waivers branch from fdd7a4d to 10b6df9 Compare May 30, 2026 18:20
baltinerdist and others added 25 commits June 3, 2026 14:01
- Awards config tab: clear the panel's unsaved-changes flag after a row save/delete, so the false 'Unsaved Changes' nag no longer appears after a successful save.
- Kingdom admin modal: add a mousedown gate to the overlay so drag-selecting text in a field (releasing outside it) no longer closes the dialog.

(Note: the related Authorization::HasAuthority parent_kingdom_id traversal that lets parent-kingdom officers manage principalities is intentionally excluded from version control by the pre-commit hook, since class.Authorization.php carries a local-only login bypass.)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Folds the standalone Principalities tab into the Parks tab on the Kingdom profile (Kingdomnew):
- Below the kingdom's own parks, each child principality is shown with its heraldry, name, and an external-link icon (links to the principality profile).
- Tile view: a grouped section per principality with its park tiles; list view: a separate table per principality with its own totals footer. Both respect the existing tile/list toggle.
- Full Avg/Wk, Avg/Mo, Total Players, Total Members reuse the existing park_averages_json endpoint per principality (a principality is a kingdom row), via a refactored reusable knFillAverages().
- Kingdom map: principality parks render as steel-blue pins (vs the kingdom's red) with a Kingdom/Principality legend, and the map sidebar shows which principality a park belongs to.

Backend: controller.Kingdom.php::profile() builds principality_parks + prinz_map_parks; KnConfig.principalityIds drives the per-principality stat fetches. New CSS is dark-mode aware. Spec: docs/superpowers/specs/2026-06-03-kingdom-principalities-in-parks-tab-design.md

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Authorization::HasAuthority now walks up parent_kingdom_id, so parent-kingdom officers (Monarch/Regent/Prime Minister) hold the same authority over a principality and its parks as over the kingdom itself. Fixes parent-kingdom officers being unable to grant awards to / configure awards for a principality.

Strictly parent->child: a top-level kingdom has parent_kingdom_id 0/NULL (stops via valid_id), with no child->sibling leak. Verified live (parent create-officer resolves TRUE for the child principality + parks, FALSE for unrelated kingdoms).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Adds the hidden ORKremental mini-game (Progress Knight port, Amtgard-themed):
- Route Reports/orkremental -> Reports_orkremental.tpl (controller.Reports.php).
- Hidden entry point: a subtle chevron link in the footer (default.theme), gated on $IsOwnProfile.
- Assets: orkremental/{game.js, classes.js, orkremental.css}.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
A kingdom could tick an award's Title checkbox and save, but on reload it
reverted to unchecked. Kingdom::GetAwardList read `ifnull(a.is_title, ka.is_title)`
and the global award's is_title column is NOT NULL, so the system value always
won and the per-kingdom `ka.is_title` override was never used.

- GetAwardList now reads ka.is_title / ka.title_class authoritatively.
- create_kingdom_awards now seeds is_title + title_class from the system award
  (was only kingdom_id, award_id, name) so new kingdoms get correct defaults.
- Backfill migration (db-migrations/2026-06-03-kingdomaward-is-title-authoritative.sql)
  sets existing ka.is_title/title_class from the linked system award, so display is
  unchanged on deploy and per-kingdom edits now stick.

After deploying + running the migration, re-apply any intended Title flags once
(previously-saved-but-ignored overrides are reset to the system default by the backfill).

Fixes the 'turn Master into a Title -> save -> reload shows it unchecked' report.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Per-kingdom admin toggle 'Include Principality in Statistics' (default off, shown
only when a kingdom has child principalities). When on, kingdom-scoped stats,
graphs, reports, counts, and the weekly recap fold in active child-principality
data via Kingdom::GetStatsKingdomIds (kingdom_id = X -> IN (family)).

- New helpers on class.Kingdom: GetChildPrincipalityIds / GetFamilyKingdomIds /
  StatsIncludesPrincipalities / GetStatsKingdomIds; admin checkbox + (?) tooltip
  in revised.js; backfill migration + CreateKingdom seed for the config.
- Rollup applied across class.Report.php (rosters, awards, recs, units, attendance
  summary/distinct/monthly, recap, park-attendance explorer, inactive parks, status
  reconciliation) and controller.Kingdom.php (park_averages_json aggregate, events,
  Parks count). Per-park LIST methods stay parent-only (principalities keep their
  separate sections) so only aggregates roll up.
- Officer Directory gains a per-principality subsection (model.Reports + controller +
  Reports_kingdomofficerdirectory.tpl), toggle-gated.
- Always-on (NOT toggle-gated): principality parks in the parks dropdown (getparks /
  GetParks KingdomIds param) and principality members in kingdom-scoped playersearch.
- Includes in-file robustness fixes to class.Kingdom: SetKingdomParent cycle guard,
  GetParks dead is_principality/parent_park_id removal, GetPrincipalities returns an
  empty success list instead of InvalidParameter.

Spec: docs/superpowers/specs/2026-06-03-include-principality-in-statistics-design.md

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
- common.php create_officers: remove the dead branch testing the undefined
  $for_principality (param is $principality_id) that would misattribute officers
  to the parent kingdom; normal officer seeding is unchanged.
- model.Principality.php get_officers/set_officers: call the Principality lib
  methods (which map PrincipalityId->KingdomId) instead of the Kingdom methods,
  which were operating on kingdom id 0.
- class.Award::LookupAward: guard the find() so a missing row returns an invalid
  kingdomaward id; class.Player::AddAward rejects a zero/invalid KingdomAwardId
  before saving, preventing orphaned award grants.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The parent-kingdom authority traversal recursed on parent_kingdom_id with only a
valid_id() gate, so a corrupt/cyclic parent (self-parent or A->B->A) could
infinite-recurse (500 / stack overflow). Thread a visited-set of kingdom ids plus
a depth cap of 10 through HasAuthority so the walk always terminates.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
GetStatsKingdomIds (and its helpers GetChildPrincipalityIds /
StatsIncludesPrincipalities) are invoked dozens of times across a single
kingdom-scoped report page. The Kingdom lib is a single instance per
request, so cache the child-principality list and the stats toggle by
kingdom id. This removes redundant DB round-trips — most beneficial for
ordinary kingdoms with no principalities, which previously paid a fresh
child-lookup query on every call site. Behavior is unchanged.

https://claude.ai/code/session_015CrcHpyb4BUfrHpW7GqgVT
Inlining the kingdom's full open-recs list (1k-4k <tr> rows on busy
kingdoms) was blocking DOMContentLoaded for 1+ second on initial
profile render. Moves the table body into a new partial
(Kingdomnew_recommendations_panel.tpl) served by
Controller_Kingdom::recommendations_panel($kid) on first tab activation;
JS (knLazyLoadRecs) swaps the placeholder in, then re-initialises the
DataTable + filter bar idempotently. Net effect on Desert Winds: 3.7MB
HTML / 4k inline <tr>s → 134KB / 0 inline rows on first paint, ~28x.

Adds cheap COUNT queries (Report::PlayerAwardRecommendationsCount and
an inline player COUNT in profile()) so the Players and Recommendations
tab badges render their numbers on first paint without waiting for the
lazy hydration. Both wrapped in ghettocache (300-600s) so re-renders are
free.

KingdomAjax::setconfig now does a full memcache->flush() on any
successful save. Kingdom::CreateKingdom / SetKingdomParent /
WaffleKingdom (activate/deactivate) do the same via a new
_flushPrincipalityCaches() helper. Kingdom config and structure changes
ripple through too many derived caches (averages, events, recs, recap,
officer directory, count badges) to enumerate; these are rare admin
actions so the wipe is acceptable.

With the lazy load in place, the principality rollup on
PlayerAwardRecommendations is restored (originally pulled back to keep
the inlined HTML small) along with its matching behavior on the new
count method, so the toggle once again affects what shows up in the
parent kingdom's Recommendations tab per the branch's spec.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Recommendations panel: adds a Park column (linked to the park profile)
between Player and Award so it's easier to tell who's from where —
ParkName/ParkId already shipped on each rec, just unused. DataTable's
date column index shifted from 4 → 5 to match.

Awards admin panel:
 - Search box above the list filters rows client-side by name / system
   name / class. Pre-lowered haystack on each <tr>'s dataset so each
   keystroke is one indexOf per row.
 - Save button per row now starts disabled, lights up the moment any
   field in the row is edited, stays disabled after a successful save
   (clean row), re-enables on a failed save for retry. Existing
   .kn-admin-tsave:disabled CSS already handles the 50% opacity dim.
 - Delete confirmation now uses the existing knConfirm() styled modal
   instead of the browser-native confirm() dialog, matching the rest
   of the kingdom admin UI.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Principalities: Parks-tab folding + statistics rollup toggle, ORKremental, and awards/auth fixes
Autonomous design for feature/digital-waivers: Kingdom-admin waiver
builder (kingdom + park scopes), player submission with drawn/typed
signature, officer verification queue and sign-off.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
TDD-driven plan: migration + skeletons (Phase 0) → domain class with 10
tested methods (Phase 1) → AJAX + page controllers (Phase 2) → five
templates with shared signature widget (Phase 3) → Kingdomnew/Parknew/
Playernew integration hooks (Phase 4) → QA + PR (Phase 5).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Adds ork_waiver_template and ork_waiver_signature tables.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Avery Krouse and others added 29 commits June 6, 2026 10:44
Minimal-chrome standalone printable page for a signed waiver. Renders
header/body/footer markdown (strip_tags for header/footer to stay
visually minimal), signer facts grid, typed-or-drawn signature, and an
officer verification block when verified. Auto-triggers window.print()
after load.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Add "Edit Waivers" and "Waiver Review Queue" links to the kingdom
admin menu dropdown so authorised kingdom admins can reach the
builder and review queue from the Kingdomnew profile page.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Load the active park-scope waiver template into $park_info['WaiverActive']
and render a "Sign Park Waiver" CTA card in the sidebar when the template
is enabled. Park admins also get a "Waiver Review Queue" link in the admin
menu dropdown.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Populate a new $_wv_sidebar data bag on Player/profile for own-profile
views and render a Digital Waivers sidebar card listing kingdom- and
park-scope active templates with sign/view links.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…view()

The View extracts controller ->data entries into local scope before including
the template, so $this->data['_wv'] resolves to null (View has no data prop).
Matches the working pattern already in place on Waiver_sign.tpl.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Amendment 1 against the v1 design spec: extend the waiver builder to cover
demographic data (DOB, address, phone, email, preferred name, gender),
emergency contact, multi-minor rosters, witness signatures, kingdom-defined
custom fields (checkbox/radio/initial/etc.), and officer ID-intake metadata.
Hybrid mechanism — first-class columns for searchable data, JSON blob only
for per-kingdom boilerplate.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Floating pencil icon appears to the left of the focused markdown textarea
(Header / Body / Minor / Footer). Hover shows an instant in-product CSS
tooltip with a markdown cheatsheet. No native title attributes.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
On one's own profile, the yellow "Needs Waiver" nameplate badge becomes a
link to /Waiver/sign/kingdom/{kingdom_id}. Other viewers still see a plain
badge (only the player can sign for themselves).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
After ctx.scale(dpr, dpr), drawing with canvas.width/height (physical px)
applied the DPR multiplier twice, shifting strokes to roughly 2x the
mouse offset on a 1.25x-2x display. Draw in CSS-unit coords (cssW/cssH)
captured at init, and apply the same pattern to the read-only
wvRenderSignature helper used in review/print.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Link default/style/reports.css and replace the raw h1/intro with the
standard rp-root > rp-header (icon + title + kingdom scope chip) and
rp-context info strip used by the Reports pages.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Adds an amber notice above the builder panes acknowledging that use of
the optional Digital Waiver module requires kingdom-side due diligence
and board approval.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Adds html[data-theme="dark"] overrides for every waiver template
(builder, sign, review, queue, signature widget) and the Parknew
sidebar waiver CTA card. Signature canvas keeps a light bg in dark
mode so the saved signature PNG remains readable everywhere.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Replaces the four markdown textareas with self-hosted Trix 2.1.15
editors and restructures the cluttered builder into three sub-tabs
per scope (Data Capture, Page Layout, Waiver Body). Side-by-side
live preview is gone; a "View Preview" button now POSTs the form
to a new Waiver/preview/{kingdom_id} route that renders the waiver
end-to-end as a signing player would see it, in a new tab.

Storage swap: ork_waiver_template columns header_markdown/body/
footer/minor renamed to *_html. PreviewMarkdown method/AJAX route
removed; a DOMDocument-based allowlist sanitizer (_sanitize_html)
strips any tag, attribute, or URL scheme outside Trix's expected
output. Sign/review/print templates now emit the stored HTML
directly. The save AJAX endpoint also now forwards MaxMinors,
CustomFieldsJson, and the Requires* flags it was silently dropping.

129 PHP tests pass including new sanitizer coverage. No production
waivers existed, so the migration renames in place rather than
dual-writing.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Adds a parallel Markdown-authored variant of the waiver builder so
the two authoring UXes can be evaluated side-by-side. Variant A is
the existing Trix WYSIWYG; variant B uses Markdown textareas with a
GitHub-style toolbar (heading, bold, italic, strike, quote, inline
code, link, bulleted + numbered lists) plus Ctrl/Cmd+B/I/K shortcuts.

ork_waiver_template gains a variant enum('a','b') column and a
parallel set of *_markdown columns. The "one active row" rule is now
keyed on (kingdom, scope, variant), so the two variants live
independently. Variant B saves both the raw markdown source AND a
sanitized HTML render (Parsedown safe-mode -> _sanitize_html), so
sign/review/print pages stay variant-agnostic and continue to read
*_html only.

Routes: Waiver/builder/{kid}[/{a|b}] and Waiver/preview/{kid}[/{a|b}]
(defaults to 'a' for backwards compatibility). Each variant page
shows a banner with a one-click link to swap variants. Admin Tasks
in Kingdomnew now exposes two entries: Manage Waivers — Rich Text
and Manage Waivers — Markdown.

129 PHP tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Each toolbar button now visually mirrors the markdown it produces:
H1/H2/H3 render as bolded "H" with descending sizes and a subscript
level; B is a bold serif "B"; I is an italic "I"; S is strikethrough;
quote is a Georgia serif quote glyph; inline code shows "</>" in
monospace; code block shows "{ }" in a pill; link is underlined blue
"link"; bullet/numbered/task lists show their actual list marker; HR
shows an em-dash line.

Adds Heading 1/2/3 (replacing the single ## button), Image, Code
Block (fenced ```), Task list (- [ ] ), and Horizontal Rule. The
Image action prompts for URL like Link does; HR inserts a fenced
"---" paragraph at the cursor.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…version metadata

Adds two officer/admin reports and the schema behind true versioning:

- Waivers Compliance report (Reports/waivers/{Type}&id=): stat cards
  (templates, signatures by status, compliance %, in-review backlog) plus a
  per-player status table, kingdom/park scope-correct. New WaiverReport domain
  class holds all read-only aggregate SQL; model passthroughs bridge to it.
- Waiver Change History report (Reports/waiverhistory/{Type}&id=[&scope=]):
  newest-first list of every template version with name, save date, author,
  status, and change reason, plus a read-only per-version content View. Kingdom
  view gets a Kingdom/Park scope toggle.
- version-metadata migration: adds version_name + change_reason to
  ork_waiver_template and backfills existing rows.
- Reports tab links on Kingdomnew/Parknew profiles for both reports.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…audit hardening

Core module changes spanning the builder, sign, and officer flows:

- Trix is the single standard builder; the Markdown A/B variant is removed
  (template, variant routing/columns, and a drop-markdown migration).
- "Publish New Version" modal on save captures a version name (default
  "YYYY-MM-DD V1", incrementing V2/V3 same day) and a change summary, pre-filled
  via WaiverAjax/versionDefaults; first-ever version pre-fills "Initial
  publication of digital waiver.". New WaiverAjax versionContent endpoint
  (IDOR-guarded) powers the read-only history View. SaveTemplate stores the
  version metadata and computes server-authoritative defaults.
- Audit hardening: submitSignature/verifySignature now forward the full
  demographic/witness/minor/custom + ID-intake field sets that the domain
  expected; SanitizeHtml made public and called consistently (fixes a latent
  cross-class private call in preview); SaveTemplate transaction wrap; GetQueue
  uses bound scope + explicit column list; printable/review auth + name
  resolution; HiDPI signature resize.
- House-rule polish: dark-mode coverage, human-readable DOB rendering,
  Flatpickr date inputs, CSS-class colors, and real data-tip tooltips.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Tracks the SanitizeHtml rename in the existing sanitizer tests and adds domain
coverage for true versioning: default name "YYYY-MM-DD V1" with same-day
increment, first-version "Initial publication of digital waiver." reason,
custom name/reason persistence, and newest-first GetVersionHistory with change
reasons and author names. Suite: 147 passed, 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@baltinerdist baltinerdist force-pushed the feature/digital-waivers branch from e2ced0c to 1516fd3 Compare June 6, 2026 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants