Halfling (Mark of Hospitality) paladin, Oath of the Ancients, level 2,
played by Gerdien. Read from photos of her printed sheet (Aoife_1-4),
which are committed alongside the other source sheets. Named "Aoife
Fermwyr" so the existing AoifeFermwyr.png portrait is picked up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The party sheets in assets/characters/ changed: Darrah_Oakwanderer.pdf is
gone, replaced by Darragh Fermwyr (same halfling ranger, renamed and
updated), Owayn Fermwyr (stout halfling fighter, BJ) and Dunnchadh
(ghostwise halfling druid, Circle of the Blighted, Rick).
- app:character:seed now carries those three sheets, read from the PDFs'
form fields. Darragh's portrait is renamed to match his new name.
- New skill expertise field (double proficiency bonus), since Darragh's
Deft Explorer gives him +7 Stealth and the sheet had nowhere to show
it. Expertise implies proficiency; the sheet marks it with two dots,
the edit form gets a checkbox list next to skill proficiencies.
- Migration adds skill_expertise, backfilling existing rows with [] so
the NOT NULL JSON column works on tables that already have characters.
Tests: expertise math on the entity, and a catalog test that every seed
sheet hydrates into a Character that passes validation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Darragh Oakwanderer never made it past the local database - the
Characters migration only creates the empty table, and I'd added him
with a one-off script that was never committed. This closes that gap
the same way app:enemy:seed does: a small catalog of ready-made sheets,
picked interactively or by name, idempotent (skips what's already
there), with --purge to remove them.
Verified against the local DB: purge, add, and a repeat add (correctly
reports 0 added / 1 already present) all produce exactly the row I'd
checked by hand earlier.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Worker::runAll() parses start/stop/restart/... off the real \$argv on
Linux - which here is Symfony console's own arguments (bin/console
app:puzzle-relay), not a command it recognizes, so it printed its
usage text and exited on every boot, and Docker kept restarting it
forever. Windows skips that argv parsing entirely, which is why this
never showed up locally. Worker::\$command = 'start' is Workerman's
own mechanism for exactly this "used as a library" case - it appends
to what getArgv() returns without touching the real \$argv.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Players open a no-login link and see live cursors and dragged gear
pieces synced in real time, backed by a small Workerman WebSocket
relay (src/Command/PuzzleRelayCommand.php) run alongside symfony
serve - no Docker/Mercure hub needed. The board renders the wall
artwork with a spare-gears tray, and a "Reset puzzle" button (visible
only to logged-in users, verified server-side via a signed token) lets
the DM wipe the shared state for everyone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The command now prompts with a numbered multi-select list instead of
seeding all monsters unconditionally. Names can also be passed as
arguments (case-insensitive) and --all keeps the old bulk behaviour for
non-interactive runs. Adds the homebrew Hearthburrow (Tiny Fey, CR 1/2)
to the catalogue, with its Cottage Actions in the legendary-actions
field and the Temper track in the description.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Parents now live in the father/mother fields, so "son/daughter/child of
X [and Y]" is redundant noise in Relationships / Notes.
- app:npc:strip-parent-text removes that clause and tidies leftover
punctuation ("Son of X, husband of Y" -> "Husband of Y"; standalone
"Son of X and Y" -> cleared). Keeps "grandson of", "father of",
"daughter-in-law of". 92 rows cleaned.
- data/npcs.csv regenerated from the DB with the cleaned text plus new
Father / Mother name columns
- app:npc:import resolves those optional columns in a second pass, so a
fresh install reproduces the parent links without app:npc:link-parents
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Npc gains self-referencing father/mother (nullable, ON DELETE SET NULL);
editable as dropdowns on the per-NPC form
- app:npc:link-parents parses "son/daughter/child of X [and Y]" from the
Relationships / Notes column, resolves the names within the same family,
and works out which parent is father vs mother from husband/wife/widow(er)
and son/daughter/sister/brother cues (plus a small name-gender table for
the few the text can't disambiguate). --dry-run / --force. 92/180 linked,
none ambiguous.
- migrations now pin ENGINE=InnoDB - the local WAMP MariaDB defaults to
MyISAM, which silently drops foreign keys
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Npc entity (village spreadsheet columns) + game_notes text field for
the DM's live play notes, plus updated_at touched on every change
- Race / VillageStatus / BloodCartInvolvement / MysteryImportance as
string-backed PHP enums, rendered as dropdowns on the edit form
- /npcs: server-side DataTables (paging, global search and column sort
all run in the backend via NpcRepository::findForDataTable); name cell
links to the edit page, game notes shown truncated
- /npcs/{id}: Bootstrap-themed edit form (all fields + a large game
notes textarea); Symfony forms now use the bootstrap_5 theme
- app.js: generic server-side DataTable wiring driven by data-* on the
<table>/<th> (data-datatable-src, data-row-url, data-format)
- app:npc:import console command - upserts from data/npcs.csv by
(family, name), never overwrites game_notes; --purge / --dry-run
- data/npcs.csv: 180 villagers exported from the working spreadsheet
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>