Skip to content

feat(parity): capability matrix for larpinq against five competitors - #730

Merged
rubenvdlinde merged 7 commits into
developmentfrom
feat/parity-capability-matrix
Sep 26, 2026
Merged

rubenvdlinde merged 7 commits into
developmentfrom
feat/parity-capability-matrix

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Adds openspec/parity/capabilities.json, larpinq's first capability matrix. The competitor columns are documentation readings. It is a data file only: no page is wired to it and no code changes.

I read the code at development a82e254. The matrix has 153 rated rows and 5 pending questions in 11 areas, rated for larpinq and five competitors.

Category

This is LARP organisation management: the back office a live-action role-play group runs its campaign on. It keeps the game world, the character sheets and the players behind them. It encodes the group's own ruleset (abilities, skills with prerequisites, items, conditions and effects) and refuses illegal builds. It awards experience after each event, and it runs the events themselves: roster, check-in, printed sheets and GM run sheets.

It is not a ticket shop, a worldbuilding wiki or a virtual tabletop. Buyers still compare it against all three for the parts that overlap.

I decided this from the code: 10 schemas in lib/Settings/larpinq_register.json and 31 pages in src/manifest.json. It is written into the file's category field so it can be corrected in one place.

What research existed

  • The research repo holds no competitor study under any name. A grep for larp, larpingapp and larpinq finds only portfolio lines.
  • market-intelligence has nothing for larpinq.
  • The intelligence database tags 20 competitors under larpingapp:
    • 10 are LARP or tabletop tools, each with about 10 generated one-line features dated 2026-04-06.
    • Kanka's 145 rows are mostly release-note lines.
    • The six added on 2026-07-23 have no features at all.
    • The tagging leaks the other way too: pretix's 35 feature rows sit under openconnector and shillinq.
  • So this is effectively a cold start with a tagged shortlist. The competitor readings were all made today, 2026-09-26, from vendor documentation.

Columns and why

key system why grade
larpmanager LarpManager The closest direct competitor. It is free and AGPL, hosted or self-hosted, and covers registration, payments, characters, XP, factions, plots and check-in. docs-only
mylarp MyLARP A hosted LARP database for campaign games: skills with prerequisites, items, events, NPCs. docs-only
larp-portal LARP Portal A US multi-game system: characters built from a rules engine, character points, registration with housing. docs-only
kanka Kanka The worldbuilding and campaign manager a group reaches for to hold characters, organisations and lore. docs-only
pretix pretix The open-source ticket shop many European groups sell event places with. docs-only

Left out:

  • Ensemble is a niche tool for learning the other characters before a game.
  • Eventbrite and Ticket Tailor are covered by pretix, the self-hostable ticketing option.
  • World Anvil, LegendKeeper, Campfire and Obsidian Portal are covered by Kanka.
  • Tabletop.Events and Warhorn schedule conventions.
  • Role Gate, Demiplane, Fantasy Grounds, Ares MU, Tavern Keeper and Chronica are for playing online or at the table.
  • Notion is generic.

MyLARP and LARP Portal publish thin documentation. That is why their columns hold 102 and 78 unknowns, each covered by a system-level unknownReason.

Licences. Kanka's licence adds a Commons Clause, so it is source-available and rates partial on adm-open-source. The pretix reader also rated pretix partial, citing "anti-SaaS terms". I corrected that cell to yes from the LICENSE text. pretix's Section 7 terms are an extra permission: they let you skip AGPL section 13 when you are not running pretix as a service. The plain AGPLv3 grant stands.

Rows

area rows
worlds 10
characters 21
players 11
rules 21
progression 15
events 19
registration 10
print 6
communication 8
insight 12
admin 20

Ratings (153 rated rows):

system yes partial no unknown
larpinq 76 17 60 0
larpmanager 70 29 37 17
mylarp 27 12 12 102
larp-portal 43 20 12 78
kanka 47 27 70 9
pretix 48 14 87 4

Where the rows came from:

  • 95 came from our own code. Of those we rate 72 yes, 12 partial and 11 no.
  • 58 came from what competitors do. Of those we rate 4 yes, 5 partial and 49 no.
  • The competitor-derived rows are where the gaps are, as expected.

Pending (5). These are questions a code reading cannot settle, so they are kept out of the rated list:

  • Two notification rows rest only on x-openregister-notifications: approval, and an event reminder.
  • The new-character submission notification.
  • Ruleset import.
  • MCP tools, which rest only on a register.d declaration. The curated hermiq-ai-tooling change is unbuilt.

Dropped at the end (9).

  • Seven duplicates where a second reader added the same capability across an area boundary.
  • Two engineering-only rows: the rename repair steps and the no-OpenRegister route fallback. Every competitor could only read "not applicable" on them.
  • The reasons are recorded in the lane's overrides file and repeated here.

Sample rows (larpinq / larpmanager / mylarp / larp-portal / kanka / pretix):

id row ratings our evidence
rul-skill-prerequisites Say which skills a character must have before learning another. yes / yes / yes / unknown / partial / no lib/Settings/larpinq_register.json:480-491, lib/Service/SkillRequirementChecker.php:111
prg-block-illegal-build Stop a character from taking a skill without its prerequisites. yes / yes / partial / unknown / no / no lib/Service/SkillRequirementChecker.php:70-99
evt-checkin Check a player's character in on arrival at the event. yes / no / partial / partial / no / yes src/views/EventRoster.vue:48-71
ins-dashboard See the campaign at a glance on a dashboard when you open the app. yes / yes / unknown / partial / yes / yes src/manifest.json:155
evt-calendar Put an event on the Nextcloud calendar. yes / yes / unknown / yes / yes / partial lib/Settings/register.d/event-calendar-leaf.json
chr-approval Send a character to the game master for approval and see whether it was approved or rejected. partial / yes / unknown / partial / no / no lib/Settings/larpinq_register.json:101-111
chr-live-stats See a character's stats recalculated from its skills, items and conditions. partial / partial / unknown / unknown / partial / no lib/Service/CharacterService.php:353-403, computed but not shown
prt-character-pdf Print a character sheet as a PDF. partial / yes / unknown / yes / no / no lib/Controller/CharactersController.php:139, no page calls it
prg-award-xp-bulk Award experience points to everyone who attended an event in one go. no / yes / unknown / partial / no / no grep, no hits
reg-online-payment Take payment for an event online. no / yes / unknown / unknown / no / yes grep, no hits

Claimed differentiators nobody has checked

These are rows where we read yes and no competitor reads yes or partial. For all but the Nextcloud-specific ones, the LARP-specific columns are mostly unknown, so they are unverified claims, not established leads:

  • chr-conditions: record the conditions affecting a character.
  • rul-define-conditions: define conditions and their effect on stats.
  • rul-effect-stacking: effects stack in a predictable order.
  • rul-skill-tree-per-character: the skill tree coloured for one character.
  • rul-skill-tree-cycle-tolerance: the skill tree survives a prerequisite cycle.
  • prg-requirement-report: see why a character cannot take a skill.
  • prg-xp-audit: every XP award, with who gave it, when and why.
  • chr-xp-award-history: per-character award history.

Nextcloud-only, and genuinely unique to the platform: ply-link-contact, ply-link-user, com-deep-links, ins-unified-search, adm-reimport-config.

Clearest gaps. These are rows where we read no and at least two of the three LARP tools read yes or partial:

  • Worlds and characters: lore wiki, sharing world information with players, character status (alive, retired, dead), custom character fields, relationships, factions.
  • Players: player self-signup, dietary and medical information.
  • Rules: a published rulebook, spell lists.
  • Progression: bulk XP for an event, players spending their own XP, XP for between-event work, attendance driving XP.
  • Events and output: crew roles, print templates.
  • Communication and reporting: mailing participants, custom reports.
  • Administration: role-based permissions.

All of registration and payment reads no.

Live-defect candidates

Each of these is a code reading and needs a live check.

  1. GM-private fields are visible. slNotesPrivate and background are described as hidden from players but sit in the CharacterDetail widgets char-progress and char-identity in src/manifest.json. The app has no property-level access control: a grep for x-property-rbac finds nothing.
  2. Two different GM checks. lib/Controller/CharactersController.php:139 (downloadPdf) checks for a Nextcloud admin. EventsController and lib/Listener/CharacterRequirementListener.php check the gamemasters group.
  3. Missing portal writer. lib/Settings/register.d/portal-identity.json says character.ownerRef is stamped by PortalObjectWriter. No such class exists in the repo, so lib/Portal/PortalContributionProvider.php would scope players by a field nothing fills.
  4. No UI for GM overrides. character.requirementOverrides is enforced and audited by CharacterRequirementListener. But it is visible:false and in no widget, so no page lets a GM create an override.
  5. Stats computed but never shown. lib/Service/CharacterService.php:353-458 computes ability values and a per-source audit trail. Its only consumer is the XP budget check in SkillRequirementService. The overlay's "transparent stat audit trail" and "self-recomputing character sheets" are listed as stable but are not reachable on any page.
  6. Uniqueness not enforced. item.unique and condition.unique are declared in lib/Settings/larpinq_register.json, but no listener enforces them.
  7. Stale settings call. src/views/settings/Settings.vue:450 calls /index.php/apps/larpingapp/api/settings/load, with the old app id, on a route found only in the fallback list in appinfo/routes.php. The caller, loadConfiguration() at line 444, is never invoked, so this is dead code rather than a visible break.
  8. Capacity and waitlist missing. openspec/specs/event-signup-to-forms-leaf/spec.md says capacity and waitlist logic stays in larpinq. A grep of lib/ for capacity and waitlist finds nothing.
  9. Skill usage spec drift. openspec/specs/larping-skill-widget/spec.md describes a GraphQL SkillUsageChart that no longer exists. src/services/graphql.js has no importers.
  10. Possible header problem. src/views/EventRoster.vue:210-220 sends OCS headers on a plain app route. This is unconfirmed.

Backends no page reaches

  • characters#downloadPdf: appinfo/routes.php:30, lib/Controller/CharactersController.php:139.

  • events#downloadRunsheet: appinfo/routes.php:31, lib/Controller/EventsController.php:105.

    Neither PDF route has a caller in src/. openspec/changes/larpinq-pdf-frontend-download-action is the open change for this.

  • CharacterService::calculateAllCharacters() (lib/Service/CharacterService.php:246-255) has no callers. Two readers found this independently.

  • /api/preferences/{key} has no literal caller in src/. nc-vue may call it internally, but that is unconfirmed.

  • src/services/graphql.js has no importers.

Where the method was harder than the skill says

  • Computed but never displayed. This is a fifth over-credit shape the skill does not name. The stat engine and the GM override are wired, and they run on every write, but their result never reaches a screen. It is not "a schema is not a screen" and not "a route no page calls", because no route is missing: the return value is simply discarded. Readers had to invent partial ratings for it. It is worth a named rule.
  • Stale starting refs. The workspace remote-tracking ref was stale too (ba97f33 against a82e254). The ls-remote check in the brief was needed, not a formality.
  • Duplicates across area boundaries. Readers split by area add the same capability from two sides: approval notification, GM override, access group, progression report. The merge step needs a duplicate pass, and seven rows were dropped here.
  • Documentation-only readings of competitor licences mislead. A reader turned pretix's extra permission into a restriction. Licence and scope cells need the source text, not a summary.
  • Thin research tagging hides in both directions. The database had generated summaries tagged in, and a competitor's real rows tagged out under other slugs.
  • Competitor seed rows about the platform read "not applicable". Rows about Nextcloud-only plumbing cannot be rated fairly against standalone products. Keep them out of the rated list, or put them in a self-only area.

Verification

python3 parity_verify.py openspec/parity/capabilities.json --strict, from hydra .claude/skills/parity-verify on development:

parity-verify: openspec/parity/capabilities.json
  153 rated rows, 5 pending, 6 systems

unknown-cells  (1)
    226 cells rate unknown. That is legitimate, and it is listed so it cannot hide: capabilities[wld-archive-campaign] larpmanager, capabilities[wld-archive-campaign] mylarp, capabilities[wld-archive-campaign] larp-portal, capabilities[wld-archive-campaign] kanka, capabilities[wld-multiple-worlds] mylarp, capabilities[wld-multiple-worlds] larp-portal, capabilities[wld-world-description] mylarp, capabilities[wld-timeline] mylarp

1 finding(s) in 1 check(s)

The only finding is the unknown census. --strict exits 1 on the census alone, by design. There are no stale-reading findings, because every column was read today.

This is a JSON file under openspec/ that no code imports, so the product's suites cannot be affected, and I did not run them.

🤖 Generated with Claude Code

…cells unknown

Systems, areas, category and the seed rows from our code and from what
competitors do. No rating yet: every cell is unknown until the readers report.
Read at development a82e254.
153 rated rows and 5 pending in 11 areas, read at development a82e254.
Competitor columns are documentation readings from 2026-09-26. Seven
cross-area duplicates and two engineering-only rows dropped; the pretix
licence cell corrected from the LICENSE text.
@rubenvdlinde
rubenvdlinde merged commit 80880a0 into development Sep 26, 2026
41 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/larpinq @ 46feb66

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-specs ✅
test-l10n ✅
format ✅
check-l10n-js ✅
check-schema-l10n ✅
composer ✅ ✅ 108/108
npm ✅ ✅ 525/525
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ⏭️ not run for this diff — no file in this diff matches the code globs, and none carries a source extension — the heavy tier has nothing to decide about it.
Newman ✅
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it on any branch from the Actions tab, or locally with npx playwright test.
Hydra gates ✅

Quality workflow — 2026-09-26 08:12 UTC

Download the full PDF report from the workflow artifacts.

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.

1 participant