Character sheets phase 2: Foundry both ways, Pathbuilder in, conversion, Update to version n #958

Closed
opened 2026-09-30 06:42:18 +00:00 by claude-bot · 10 comments
Collaborator

Deferred from lane 11 (#737) by D200. Phase 2 of docs/design/character-sheets-spec.md §7.

Scope, all drawn on lane9.html but absent from v4.5.0, as D36 requires:

  • IS-427: import a sheet from a Foundry actor's Export Data, with the pack's imports.foundry map and provenance in source_ref, so a re-import updates (D82).
  • IS-428, the second half: export a sheet as a Foundry actor where the pack ships exports.foundry. JSON export and Print ship in lane 11.
  • IS-429: Pathbuilder JSON in, for PF2e.
  • IS-424: convert a sheet from another system into the campaign's definition, as a draft for review (D5).
  • IS-423: "Update to version n": a definition's migrations applied when the GM chooses, never forced (D83).

Related, owned elsewhere: the Foundry member panel on Members & characters (CAMP-8, IS-272–275) and Settings → Foundry (CAMP-16, IS-268–271) belong to v5.0.0 (#5, #6, #8), per #737's comments.

Phase 3 (the CLI validator with fixtures, the community packs, the author's guide, amend overlays, the group sheet IS-436) comes after this.

Deferred from lane 11 (#737) by **D200**. Phase 2 of `docs/design/character-sheets-spec.md` §7. **Scope, all drawn on lane9.html but absent from v4.5.0, as D36 requires:** - **IS-427:** import a sheet from a Foundry actor's Export Data, with the pack's `imports.foundry` map and provenance in `source_ref`, so a re-import updates (D82). - **IS-428, the second half:** export a sheet as a Foundry actor where the pack ships `exports.foundry`. JSON export and Print ship in lane 11. - **IS-429:** Pathbuilder JSON in, for PF2e. - **IS-424:** convert a sheet from another system into the campaign's definition, as a draft for review (D5). - **IS-423:** "Update to version n": a definition's `migrations` applied when the GM chooses, never forced (D83). **Related, owned elsewhere:** the Foundry member panel on Members & characters (CAMP-8, IS-272–275) and Settings → Foundry (CAMP-16, IS-268–271) belong to **v5.0.0** (#5, #6, #8), per #737's comments. Phase 3 (the CLI validator with fixtures, the community packs, the author's guide, `amend` overlays, the group sheet IS-436) comes after this.
Author
Collaborator

Owner, 27 Oct 2026: this moves to v4.8.0 - Rules at the Table, without the Foundry half. IS-427 (import from a Foundry actor) and the Foundry export in IS-428 are split into #1249 in v5.0.0, with the Foundry work. Left here: IS-429 Pathbuilder in, IS-424 conversion, IS-423 Update to version n.

Owner, 27 Oct 2026: this moves to **v4.8.0 - Rules at the Table**, without the Foundry half. IS-427 (import from a Foundry actor) and the Foundry export in IS-428 are split into #1249 in v5.0.0, with the Foundry work. Left here: IS-429 Pathbuilder in, IS-424 conversion, IS-423 Update to version n.
Owner

I've created a sample Pathbuilder character, at level 8. Its character sheet can be loaded from https://pathbuilder2e.com/launch.html?build=1623583, the link to the JSON export is https://pathbuilder2e.com/json.php?id=484225 (users are just provided the code part of that URL, 484225), and I've attached a couple of additional types of export as well since players may use any of these methods to import the character into Quest Board.

I've created a sample Pathbuilder character, at level 8. Its character sheet can be loaded from `https://pathbuilder2e.com/launch.html?build=1623583`, the link to the JSON export is `https://pathbuilder2e.com/json.php?id=484225` (users are just provided the code part of that URL, `484225`), and I've attached a couple of additional types of export as well since players may use any of these methods to import the character into Quest Board.
Author
Collaborator

What the four Pathbuilder exports give us (looked at 2026-11-06):

Export What it is Import
Character sheet PDF The official fillable PF2e sheet, filled in by Pathbuilder (pdf-lib): 1,087 named form fields, 240 filled Reliable. It has name, ancestry, heritage, background, class, level, deity, every attribute, AC and its parts, saves with ranks, perception, speed, class DC, HP, every skill with rank and parts, both lores with names, senses, languages, and strikes with attack, damage and traits. Read with pypdf's form fields: no text scraping, no AI.
Stat block PDF A browser print of Pathbuilder's stat block page (cairo, from Firefox): plain text, headings doubled letter by letter for bold Possible but fragile: scraping laid-out text breaks whenever Pathbuilder changes its page. It holds feats, specials and the actions' text, which the sheet PDF has only partly.
JSON code (json.php?id=484225) Pathbuilder's own build JSON, the richest source Blocked from servers: pathbuilder2e.com answers 403 with a Cloudflare "Just a moment…" challenge to a fetch from a cloud machine. Whether it also blocks a fetch from a home connection wasn't tested (that test needs the owner's OK). A player can always open the link in their own browser and paste or upload what it shows.
Share link (launch.html?build=1623583) Opens the build in Pathbuilder's own app Not usable by itself: the build number isn't the JSON code, and the page is behind the same challenge.

Proposal:

  1. The sheet PDF first. It's what any player can make, and it's reliable.
  2. JSON by paste or upload, plus a server-side fetch of the code if that turns out to work from a self-hosted server. Never depend on that fetch for the hosted service.
  3. No stat-block scraping or share links for now.

Every import shows what was read and what wasn't (D82) and lands as a sheet a GM or player can correct. Questions for the owner are on the Questions page (CS2, CS3).

**What the four Pathbuilder exports give us (looked at 2026-11-06):** | Export | What it is | Import | |---|---|---| | **Character sheet PDF** | The official fillable PF2e sheet, filled in by Pathbuilder (pdf-lib): **1,087 named form fields, 240 filled** | **Reliable.** It has name, ancestry, heritage, background, class, **level**, deity, every attribute, AC and its parts, saves with ranks, perception, speed, class DC, HP, every skill with rank and parts, both lores with names, senses, languages, and strikes with attack, damage and traits. Read with pypdf's form fields: no text scraping, no AI. | | **Stat block PDF** | A browser print of Pathbuilder's stat block page (cairo, from Firefox): plain text, headings doubled letter by letter for bold | Possible but fragile: scraping laid-out text breaks whenever Pathbuilder changes its page. It holds feats, specials and the actions' text, which the sheet PDF has only partly. | | **JSON code** (`json.php?id=484225`) | Pathbuilder's own build JSON, the richest source | **Blocked from servers:** pathbuilder2e.com answers 403 with a Cloudflare "Just a moment…" challenge to a fetch from a cloud machine. Whether it also blocks a fetch from a home connection wasn't tested (that test needs the owner's OK). A player can always open the link in their own browser and paste or upload what it shows. | | **Share link** (`launch.html?build=1623583`) | Opens the build in Pathbuilder's own app | Not usable by itself: the build number isn't the JSON code, and the page is behind the same challenge. | **Proposal:** 1. **The sheet PDF first.** It's what any player can make, and it's reliable. 2. **JSON by paste or upload,** plus a server-side fetch of the code if that turns out to work from a self-hosted server. Never depend on that fetch for the hosted service. 3. **No stat-block scraping or share links** for now. Every import shows what was read and what wasn't (D82) and lands as a sheet a GM or player can correct. Questions for the owner are on the Questions page (CS2, CS3).
Owner

Here's the saved JSON.

Here's the saved JSON.
Author
Collaborator

The JSON code: tested and settled (owner's OK, 2026-11-06).

  • Server fetch is blocked even from home: one request from docker-test, on the owner's network, to json.php?id=484225 got 403 with cf-mitigated: challenge and server: cloudflare. Servers can't fetch it, home-hosted or not.
  • How Pathmuncher does it: Pathmuncher.fetchPathbuilder() calls foundry.utils.fetchJsonWithTimeout(\https://pathbuilder2e.com/json.php?id=${pathbuilderId}`)`. That runs in the player's browser, because Foundry modules are client-side, so it's a cross-site fetch from a real browser.
  • So Quest Board does the same: its page fetches json.php?id=<code> in the player's browser and posts the JSON to the server. The server never calls Pathbuilder. If a browser fetch fails (the challenge, or CORS), the page falls back to "open this link, then paste or upload what it shows". Still to confirm in a real browser: that json.php sends CORS headers that allow the read, as Pathmuncher's working import implies.
  • Sample JSON attached (Skell Eton): Pathbuilder's standard build format ({success, build:{…}}), richer than the PDF. For example, it has all 4 lores where the PDF has 2 slots.

Building now: the backend importer for the sheet PDF and the JSON, with a preview and report before saving and re-import that updates the same sheet. The frontend follows.

**The JSON code: tested and settled (owner's OK, 2026-11-06).** - **Server fetch is blocked even from home:** one request from docker-test, on the owner's network, to `json.php?id=484225` got 403 with `cf-mitigated: challenge` and `server: cloudflare`. Servers can't fetch it, home-hosted or not. - **How Pathmuncher does it:** `Pathmuncher.fetchPathbuilder()` calls `foundry.utils.fetchJsonWithTimeout(\`https://pathbuilder2e.com/json.php?id=${pathbuilderId}\`)`. That runs in the player's browser, because Foundry modules are client-side, so it's a cross-site fetch from a real browser. - **So Quest Board does the same:** its page fetches `json.php?id=<code>` in the player's browser and posts the JSON to the server. The server never calls Pathbuilder. If a browser fetch fails (the challenge, or CORS), the page falls back to "open this link, then paste or upload what it shows". Still to confirm in a real browser: that json.php sends CORS headers that allow the read, as Pathmuncher's working import implies. - **Sample JSON attached (Skell Eton):** Pathbuilder's standard build format (`{success, build:{…}}`), richer than the PDF. For example, it has all 4 lores where the PDF has 2 slots. **Building now:** the backend importer for the sheet PDF and the JSON, with a preview and report before saving and re-import that updates the same sheet. The frontend follows.
Author
Collaborator

Browser fetch confirmed (owner's console, 2026-11-07): fetch("https://pathbuilder2e.com/json.php?id=484225").then(r => r.json()) printed Skell Eton. Pathbuilder allows the cross-site read, and Cloudflare lets a real browser through, as for Pathmuncher. So the frontend fetches the JSON by code in the player's browser and posts it to …/sheet/import/pathbuilder (#1361). It must add https://pathbuilder2e.com to connect-src in webapp/frontend/nginx.conf, which is currently 'self' only. Paste and upload remain the fallback.

**Browser fetch confirmed (owner's console, 2026-11-07):** `fetch("https://pathbuilder2e.com/json.php?id=484225").then(r => r.json())` printed `Skell Eton`. Pathbuilder allows the cross-site read, and Cloudflare lets a real browser through, as for Pathmuncher. So the frontend fetches the JSON by code in the player's browser and posts it to `…/sheet/import/pathbuilder` (#1361). It must add `https://pathbuilder2e.com` to `connect-src` in `webapp/frontend/nginx.conf`, which is currently `'self'` only. Paste and upload remain the fallback.
Author
Collaborator

Correction to the comment above (owner, 2026-11-07):

  • Where it ran: the console check that printed Skell Eton was run on foundry-dev, not Quest Board. It still shows that Pathbuilder allows a cross-site read from a real browser on another origin, and that Cloudflare passes it.
  • On Quest Board dev, the same fetch is blocked by Quest Board's own CSP (connect-src 'self' in webapp/frontend/nginx.conf), as expected.
  • So the frontend change must add https://pathbuilder2e.com to connect-src. That one origin only, and only for connect-src. After that, the browser fetch by code works as in Pathmuncher.
**Correction to the comment above (owner, 2026-11-07):** - **Where it ran:** the console check that printed `Skell Eton` was run on **foundry-dev**, not Quest Board. It still shows that Pathbuilder allows a cross-site read from a real browser on another origin, and that Cloudflare passes it. - **On Quest Board dev, the same fetch is blocked by Quest Board's own CSP** (`connect-src 'self'` in `webapp/frontend/nginx.conf`), as expected. - **So the frontend change must add `https://pathbuilder2e.com` to `connect-src`.** That one origin only, and only for `connect-src`. After that, the browser fetch by code works as in Pathmuncher.
Author
Collaborator

Pathbuilder import is complete on main (2026-11-07):

  • Backend: #1361.
  • Frontend: #1363, with the owner's wording (part z, all 19 approved). "Import from Pathbuilder…" offers:
    • Code: fetched in the player's browser, with the CSP's connect-src now allowing https://pathbuilder2e.com only;
    • Paste or file;
    • Character sheet PDF.
  • Preview first: it shows what was read, what wasn't, matched and kept-as-text entries, and numbers that differ, before anything saves. Re-import updates the same sheet. GM locks are respected, and PDF reads are bounded.

Still open here: "Update to version n" (IS-423) and converting a sheet from another system (IS-424), both being built. Converting got a security fix mid-build: a player can't shed GM locks by converting.

**Pathbuilder import is complete on main (2026-11-07):** - **Backend:** #1361. - **Frontend:** #1363, with the owner's wording (part z, all 19 approved). "Import from Pathbuilder…" offers: - **Code:** fetched in the player's browser, with the CSP's `connect-src` now allowing `https://pathbuilder2e.com` only; - **Paste or file;** - **Character sheet PDF.** - **Preview first:** it shows what was read, what wasn't, matched and kept-as-text entries, and numbers that differ, before anything saves. Re-import updates the same sheet. GM locks are respected, and PDF reads are bounded. **Still open here:** "Update to version n" (IS-423) and converting a sheet from another system (IS-424), both being built. Converting got a security fix mid-build: a player can't shed GM locks by converting.
rbrooks referenced this issue from a commit 2026-11-07 04:50:56 +00:00
rbrooks referenced this issue from a commit 2026-11-07 04:50:56 +00:00
Author
Collaborator

Decided (owner, 2026-11-07, Q249 CV1; to be recorded as D341): a sheet conversion's draft stays in the dialog, as built in #1365. It's reviewed on the spot and isn't stored, so it can't go stale. Lane 9's caption, which put the draft in Suggested wiki updates, is superseded for conversion. "Save for later" can be added later if it's missed.

Wording: parts "aa" (Update to version n, #1364) and "ab" (conversion, #1365) are all approved.

Render checks after fixes:

  • #1364: one named region, and the empty state is shown.
  • #1365: pinned dialog buttons, long values wrap, rows line up. axe is clean at 390 and 1280 in light and dark.

Both merge on green.

**Decided (owner, 2026-11-07, Q249 CV1; to be recorded as D341):** a sheet conversion's draft stays in the dialog, as built in #1365. It's reviewed on the spot and isn't stored, so it can't go stale. Lane 9's caption, which put the draft in Suggested wiki updates, is superseded for conversion. "Save for later" can be added later if it's missed. **Wording:** parts "aa" (Update to version n, #1364) and "ab" (conversion, #1365) are all approved. **Render checks after fixes:** - #1364: one named region, and the empty state is shown. - #1365: pinned dialog buttons, long values wrap, rows line up. axe is clean at 390 and 1280 in light and dark. Both merge on green.
Author
Collaborator

Done for v4.8.0 (2026-11-07). All three parts kept here after the Foundry half moved to #1249 are on main.

  • IS-429, Pathbuilder in (#1361 backend, #1363 frontend): "Import from Pathbuilder…" takes a code (fetched in the player's browser; connect-src allows https://pathbuilder2e.com only), pasted or uploaded JSON, or the character sheet PDF. A preview shows what was read and what wasn't before anything saves. Re-importing updates the same sheet, and GM locks are respected.
  • IS-424, conversion (#1365): a sheet saved for another game can be converted into the campaign's definition by the campaign's AI. The draft is checked in the dialog, where each value can be edited or left out, entries are matched to the rules or kept as text, and the reasons for anything not converted are shown. One confirm moves the sheet over, and the old sheet stays in Versions. A player can't convert past a GM's lock, and a version from before a conversion can't be put back onto the converted sheet. Where the draft lives is recorded as D341 (#1366).
  • IS-423, Update to version n (#1364): a GM sees "Version n is ready" with What changes and applies it when ready. It is never forced. The sheet as it was is kept as a migration version.

Wording: parts z, aa and ab, all approved. Render checks passed on all three, axe clean.

Follow-ups: none blocking. A "Save for later" for conversion drafts can be added if it's missed (D341). The Foundry import and export are #1249 (v5.0.0).

**Done for v4.8.0 (2026-11-07).** All three parts kept here after the Foundry half moved to #1249 are on main. - **IS-429, Pathbuilder in** (#1361 backend, #1363 frontend): "Import from Pathbuilder…" takes a code (fetched in the player's browser; `connect-src` allows `https://pathbuilder2e.com` only), pasted or uploaded JSON, or the character sheet PDF. A preview shows what was read and what wasn't before anything saves. Re-importing updates the same sheet, and GM locks are respected. - **IS-424, conversion** (#1365): a sheet saved for another game can be converted into the campaign's definition by the campaign's AI. The draft is checked in the dialog, where each value can be edited or left out, entries are matched to the rules or kept as text, and the reasons for anything not converted are shown. One confirm moves the sheet over, and the old sheet stays in Versions. A player can't convert past a GM's lock, and a version from before a conversion can't be put back onto the converted sheet. Where the draft lives is recorded as **D341** (#1366). - **IS-423, Update to version n** (#1364): a GM sees "Version n is ready" with What changes and applies it when ready. It is never forced. The sheet as it was is kept as a `migration` version. Wording: parts z, aa and ab, all approved. Render checks passed on all three, axe clean. **Follow-ups:** none blocking. A "Save for later" for conversion drafts can be added if it's missed (D341). The Foundry import and export are #1249 (v5.0.0).
Sign in to join this conversation.
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#958
No description provided.