Skip to content

Commit bbb825e

Browse files
authored
Merge pull request #134 from keyxmakerx/claude/foundry-module-review-IrVxd
Claude/foundry module review ir vxd
2 parents a77f890 + 3068c7a commit bbb825e

22 files changed

Lines changed: 2207 additions & 207 deletions

.ai/status.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,33 @@
88
<!-- ====================================================================== -->
99

1010
## Last Updated
11-
2026-03-11 -- **Foundry module review — bug fixes and feature completion.**
11+
2026-03-12 -- **Sprint F-4 done + F-4.5 planned.**
12+
13+
35. **F-4.5 planning: Generic System Adapter & Dynamic Matching.**
14+
- Identified that F-4's `SYSTEM_MAP` and `_loadAdapter()` switch are hardcoded to only dnd5e/pf2e/drawsteel. Custom-uploaded game systems can't participate in character sync despite having the server infrastructure (entity presets, `CharacterPreset()` helper, campaign system upload).
15+
- Planned F-4.5 sprint: add `foundry_system_id` to system manifest, add `foundry_path` + `foundry_writable` annotations on character preset field definitions, new `GET /systems/:id/character-fields` API endpoint, new `generic-adapter.mjs` that reads field definitions from API and auto-generates field mappings. dnd5e/pf2e remain as overrides.
16+
- Updated `.ai/todo.md`, `foundry-module/.ai.md` (known limitations + F-4.5 plan), and Phase F master plan with full F-4.5 sprint spec.
17+
18+
34. **Sprint F-4: Actor ↔ Entity Sync (DONE).**
19+
- **actor-sync.mjs** — New `ActorSync` module class. Bidirectional sync between Foundry Actors (type: character) and Chronicle character entities. Registers `createActor`/`updateActor`/`deleteActor` hooks. Handles `entity.created/updated/deleted` WS messages filtered by character type. Uses `_syncing` guard. `_onCharacterDeleted()` unlinks (unsets flags) rather than deleting Actor.
20+
- **System adapters**`adapters/dnd5e-adapter.mjs` maps 15 D&D 5e fields (ability scores, HP, AC, speed, level, class, race, alignment, proficiency_bonus). `adapters/pf2e-adapter.mjs` maps PF2e fields (ability mods, HP, AC, perception, ancestry, heritage); only pushes HP/name back to Foundry (PF2e derives most values from items/rules).
21+
- **Dashboard Characters tab** — New tab in sync dashboard showing synced/unlinked actors with Push button for manual push. Empty states for no actors, disabled sync, no system match.
22+
- **module.mjs** — Registered `ActorSync` as sync module.
23+
- **TESTING.md** — Added 30+ character sync test items covering both directions, dashboard, adapters, edge cases.
24+
- **Next:** F-5 (NPC Viewer / Hall) or F-6 (Armory / Inventory).
25+
26+
33. **Sprint F-3: System detection & character field templates (DONE).**
27+
- **Server: Manifest expansion** — dnd5e character preset expanded from 4 to 15 fields (added ability scores, HP, AC, speed, proficiency_bonus). New pf2e character preset with 15 PF2e-specific fields (ancestry, heritage, ability mods, perception, etc). Added `CharacterPreset()` method on `SystemManifest`.
28+
- **Server: Systems API** — New `GET /api/v1/campaigns/:id/systems` endpoint returning all registered systems with `enabled` flag per campaign (via `AddonChecker`). `addonChecker` injected into `APIHandler` via `SetAddonChecker()`.
29+
- **Foundry: System detection**`SYSTEM_MAP` maps Foundry `game.system.id` → Chronicle system IDs (`dnd5e`, `pf2e`, `drawsteel`). `SyncManager._detectSystem()` queries systems API on start, stores matched system in `detectedSystem` setting. New `syncCharacters` boolean setting (gated on system match).
30+
- **Foundry: Dashboard** — Status tab shows Foundry system, Chronicle system match (green check/red X), and character sync availability.
31+
- **Next:** F-4 (Actor ↔ Entity Sync) — new `actor-sync.mjs` with system-specific adapters.
32+
33+
32. **Foundry enhancements — planning + F-1/F-2 implementation.**
34+
- **Planning:** Captured Phase F roadmap (F-1 through F-7) in `.ai/todo.md` and `foundry-module/.ai.md`.
35+
- **F-1: Journal sync fidelity (DONE):** Multi-page sync — entity content with h1/h2 headings splits into separate Foundry pages via `_splitByHeadings()`. Multiple Foundry pages concatenate back into single Chronicle entry via `_collectTextPages()`. `_syncPagesToJournal()` adds/updates/removes pages incrementally. Ownership change hook now pushes `is_private` on every update.
36+
- **F-2: Granular permission mapping (DONE):** New syncapi endpoints `GET/PUT /entities/:eid/permissions` wrapping existing `EntityService.GetEntityPermissions` / `SetEntityPermissions`. Foundry module: `_buildOwnership()` fetches Chronicle permissions and maps role grants to Foundry default ownership levels (custom visibility player view→OBSERVER, player edit→OWNER, no player grant→NONE). `_pushPermissions()` reverse-maps Foundry ownership changes to Chronicle visibility/permission updates. User-specific grants stored but not mapped (needs user ID mapping table — deferred). TESTING.md updated with multi-page and permission test items.
37+
- **Remaining planned:** F-3 (system detection), F-4 (actor sync), F-5 (NPC hall), F-6 (armory/inventory), F-7 (shop enhancements).
1238

1339
31. **Foundry module review.** Comprehensive code review of the Foundry VTT sync module found 13 issues. Fixed 9 (deferred ApplicationV2 upgrade):
1440
- **Runtime bugs**: Shop window `{{json}}` helper crash (replaced with data-item-id lookup), drawing coordinate conversion missing percentage↔pixel (tokens had it, drawings didn't), fog reconciliation `_syncing` flag corruption (extracted `_createFogDrawingData`, batch creates), entity_type_id:0 invalid in syncapi handler (added first-type fallback).

.ai/todo.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,19 @@ _WASM-sandboxed backend logic via Extism/wazero. See ADR-021._
279279
- [x] **Sprint R-3: Write Host Functions** — 6 write host functions (update_entity_fields, create_event, set_entity_tags, get_entity_tags, create_relation, send_message). 5 new capabilities. 4 write adapters. Plugin-to-plugin async messaging. 10 new tests (48 total).
280280
- [x] **Sprint R-4: Plugin SDK & Developer Tools** — Example WASM plugins (Rust auto-tagger, Go session-logger). Go SDK with MockHost test harness (9 tests). Plugin development guide. 7 new manifest tests. **Phase R complete.**
281281

282+
### Phase F: Foundry Sync Enhancements & Character Integration
283+
284+
_Improve Foundry VTT sync fidelity. Add system-aware character sheet sync. Build toward inventory/NPC features._
285+
286+
- [x] **Sprint F-1: Journal Sync Fidelity** — Multi-page journal sync (split entity `entry_html` by headings into Foundry pages, concatenate pages back on Foundry→Chronicle). Ownership change hook (detect ownership changes in `updateJournalEntry` hook, push to Chronicle). Helpers: `_splitByHeadings`, `_collectTextPages`, `_syncPagesToJournal`.
287+
- [x] **Sprint F-2: Granular Permission Mapping** — Map Chronicle `visibility: 'custom'` + `entity_permissions` to Foundry per-user ownership levels (view→OBSERVER, edit→OWNER). New syncapi endpoints: `GET /entities/:eid/permissions`, `PUT /entities/:eid/permissions`. Reverse-map Foundry ownership changes back to Chronicle. Helpers: `_buildOwnership`, `_pushPermissions`. User-specific grants stored in flags but not mapped to Foundry users (requires user ID mapping table — deferred).
288+
- [x] **Sprint F-3: System Detection & Character Field Templates** — Expanded dnd5e character preset (15 fields: class, level, race, alignment + 6 ability scores + HP/AC/speed/proficiency). Added pf2e character preset (15 fields: class, level, ancestry, heritage + 6 ability mods + HP/AC/perception/speed). `CharacterPreset()` helper on `SystemManifest`. New `GET /api/v1/campaigns/:id/systems` endpoint returns available systems with enabled flag. Foundry module: `syncCharacters` + `detectedSystem` settings, `SYSTEM_MAP` table, `_detectSystem()` on start, `getMatchedSystem()` accessor. Dashboard Status tab shows system match info and character sync availability.
289+
- [x] **Sprint F-4: Actor ↔ Entity Sync**`actor-sync.mjs` with bidirectional Actor ↔ entity sync. System adapters: `dnd5e-adapter.mjs` (15 fields), `pf2e-adapter.mjs` (HP/name back only). Dashboard Characters tab with Push button. Registered in module.mjs. TESTING.md updated. **Note: adapters and SYSTEM_MAP are hardcoded — see F-4.5.**
290+
- [ ] **Sprint F-4.5: Generic System Adapter & Dynamic Matching** — Remove hardcoded `SYSTEM_MAP` and adapter switch. Instead: (1) Add `foundry_system_id` field to system manifest schema so custom-uploaded systems can declare Foundry compatibility. (2) `_detectSystem()` queries API and matches by `foundry_system_id` instead of static JS map. (3) Add `foundry_path` annotation on character preset field definitions (e.g., `"foundry_path": "system.abilities.str.value"`). (4) New `generic-adapter.mjs` reads character preset fields from API, auto-generates `toChronicleFields()`/`fromChronicleFields()` using `foundry_path` annotations. Fields without `foundry_path` are read-only (pushed to Chronicle but not written back to Foundry). (5) dnd5e/pf2e adapters remain as overrides for edge cases. **Result: any user-uploaded custom game system with a character preset and foundry_path annotations gets automatic character sync.**
291+
- [ ] **Sprint F-5: NPC Viewer / Hall** — Campaign route `/campaigns/:id/npcs`. Gallery/grid of revealed NPCs (non-private character entities). Portrait, name, description, location, faction. Filters by location/organization/relation. "Reveal" = DM toggles `is_private`. Foundry integration: ownership change on NPC journal → auto-reveal on Chronicle. Long-term: NPC relationship map (filtered relation graph).
292+
- [ ] **Sprint F-6: Armory / Inventory System** — Items as entities with game-mechanic fields (weight, cost, rarity, damage, properties). Character "Inventory" tab/block via entity relations. Relation metadata: equipped, quantity, attunement. System-specific item templates (dnd5e ≠ pf2e). Foundry sync: Actor inventory ↔ Chronicle inventory relations. "Armory" campaign page showing all catalogued items.
293+
- [ ] **Sprint F-7: Shop / Marketplace Enhancement** — Transaction logging (who bought what, when). Currency tracking per character. Stock management (auto-deplete on purchase). Foundry: purchase from shop window → update character inventory on both sides.
294+
282295
### Deferred to Phase S+ (or community contributions)
283296

284297
- [ ] **Module Builder UI** — Guided wizard that helps users create custom game system modules through the web UI. Step-by-step: name/metadata → define categories → define fields per category → paste/upload reference data → preview tooltips → export as module directory. Eliminates need to hand-write manifest.json + data files.

foundry-module/.ai.md

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ module.mjs (entry point)
1515
├─ JournalSync (journal-sync.mjs) ← Entity ↔ JournalEntry
1616
├─ MapSync (map-sync.mjs) ← Map drawings/tokens ↔ Scene
1717
├─ CalendarSync (calendar-sync.mjs)← Calendar events/date ↔ Calendaria/SimpleCalendar
18-
└─ ShopWidget (shop-widget.mjs) ← Shop entity inventory in Foundry UI
18+
├─ ShopWidget (shop-widget.mjs) ← Shop entity inventory in Foundry UI
19+
└─ ActorSync (actor-sync.mjs) ← Character entity ↔ Actor
20+
├─ dnd5e-adapter.mjs ← D&D 5e field mapping
21+
└─ pf2e-adapter.mjs ← Pathfinder 2e field mapping
1922
```
2023

2124
### Data Flow
@@ -39,17 +42,20 @@ All sync modules use a `_syncing` boolean flag to prevent infinite loops:
3942
|------|---------|
4043
| `module.json` | Module manifest. Foundry v12-v13 compatible. Optional deps: Calendaria, Monk's Enhanced Journal |
4144
| `scripts/module.mjs` | Entry point. Registers settings on `init`, starts SyncManager on `ready` (GM only) |
42-
| `scripts/settings.mjs` | 7 world-scoped settings: apiUrl, apiKey, campaignId, syncEnabled, feature toggles |
45+
| `scripts/settings.mjs` | 9 world-scoped settings: apiUrl, apiKey, campaignId, syncEnabled, syncJournals, syncMaps, syncCalendar, syncCharacters + 3 internal: lastSyncTime, syncExclusions, detectedSystem |
4346
| `scripts/api-client.mjs` | REST client (Bearer auth) + WebSocket (auto-reconnect, message queue, event emitter) |
4447
| `scripts/sync-manager.mjs` | Orchestrator. Owns API client, routes WS messages, manages sync mapping lookups |
4548
| `scripts/journal-sync.mjs` | Entity ↔ JournalEntry bidirectional sync. Monk's Enhanced Journal support |
4649
| `scripts/map-sync.mjs` | Map drawings/tokens/fog sync. Percentage↔pixel coordinate conversion. Scene-to-map linking via context menu |
4750
| `scripts/calendar-sync.mjs` | Adapter pattern for Calendaria and SimpleCalendar. 0-indexed↔1-indexed conversion |
4851
| `scripts/shop-widget.mjs` | Shop window (Application v1). Context menu, drag-to-character-sheet, real-time updates |
52+
| `scripts/actor-sync.mjs` | Actor ↔ character entity bidirectional sync. System adapter loading, hook registration |
53+
| `scripts/adapters/dnd5e-adapter.mjs` | D&D 5e field mapping: 15 fields (ability scores, HP, AC, speed, level, class, race, alignment, proficiency_bonus) |
54+
| `scripts/adapters/pf2e-adapter.mjs` | PF2e field mapping: ability mods, HP, AC, perception, speed, level, class, ancestry, heritage. Only HP/name sync back |
4955
| `templates/shop-window.hbs` | Handlebars template for shop inventory display |
5056
| `styles/chronicle-sync.css` | Status indicator + shop window styles |
5157
| `lang/en.json` | English localization for all UI strings |
52-
| `TESTING.md` | 116-item E2E testing checklist |
58+
| `TESTING.md` | E2E testing checklist (journals, maps, calendar, shops, characters) |
5359

5460
## Server-Side Interconnects
5561

@@ -92,3 +98,62 @@ All sync modules use a `_syncing` boolean flag to prevent infinite loops:
9298
- **Shop icon field**: Always returns null (`shop-widget.mjs:146`)
9399
- **Single scene**: Only the active Foundry scene syncs (no multi-scene)
94100
- **GM only**: Full sync runs only for GM users; players get passive updates via Foundry
101+
- **Character sync: limited fields back from PF2e**: Only HP and name sync from Chronicle to PF2e actors (most values are derived from items/rules)
102+
- **Character sync: hardcoded adapters**: `_loadAdapter()` in actor-sync.mjs uses a switch statement that only loads dnd5e and pf2e adapters. Custom/uploaded game systems won't get character sync until F-4.5 (generic adapter) is implemented. The server infrastructure already supports it — custom systems can define `entity_presets` with a `-character` slug and field definitions. F-4.5 will replace the hardcoded switch with a generic adapter that reads field mappings from the Chronicle API.
103+
- **System ID mapping hardcoded**: `SYSTEM_MAP` in sync-manager.mjs only maps 3 Foundry system IDs. F-4.5 will query available systems from the API and match dynamically.
104+
105+
## Planned Features (Phase F)
106+
107+
See `.ai/todo.md` Phase F for full sprint breakdown.
108+
109+
### F-1: Journal Sync Fidelity (DONE)
110+
- Split entity `entry_html` by `<h1>`/`<h2>` headings into multiple Foundry pages
111+
- Concatenate all text pages back into single `entry` for Foundry→Chronicle
112+
- Track page mapping via `chronicle-sync.pageMap` flag
113+
- Detect ownership changes in `updateJournalEntry` hook, push to Chronicle
114+
115+
### F-2: Granular Permission Mapping (DONE)
116+
- Map Chronicle `visibility: 'custom'` + `entity_permissions` to per-user Foundry ownership
117+
- Chronicle `view` → Foundry `OBSERVER`, `edit``OWNER`
118+
- New syncapi endpoints: `GET/PUT /entities/:eid/permissions`
119+
- Reverse-map Foundry ownership changes to Chronicle entity permissions
120+
121+
### F-3: System Detection & Character Field Templates (DONE)
122+
- `SYSTEM_MAP` in sync-manager.mjs: `{ dnd5e: "dnd5e", pf2e: "pathfinder2e", drawsteel: "drawsteel" }`
123+
- `SyncManager._detectSystem()` queries `GET /api/v1/campaigns/:id/systems` on start
124+
- Matches Foundry `game.system.id` to Chronicle system, stores in `detectedSystem` setting
125+
- `syncCharacters` boolean setting (gated on system match)
126+
- Dashboard Status tab shows system match info and character sync availability
127+
- Server: Expanded dnd5e character preset (15 fields), added pf2e character preset (15 fields)
128+
- Server: `CharacterPreset()` helper on `SystemManifest`
129+
- Server: New `GET /systems` API endpoint with `enabled` flag per campaign
130+
131+
### F-4: Actor ↔ Entity Sync (DONE)
132+
- `actor-sync.mjs`: Bidirectional sync between Foundry Actors and Chronicle character entities
133+
- System adapters: `adapters/dnd5e-adapter.mjs` (15 fields), `adapters/pf2e-adapter.mjs` (HP/name back only)
134+
- Hooks: `createActor`, `updateActor`, `deleteActor` with `_syncing` guard
135+
- WS: `entity.created/updated/deleted` filtered by character type (via type_slug, type_name, or type_id)
136+
- Dashboard "Characters" tab: synced/unlinked actors, Push button, empty states
137+
- Delete from Chronicle unlinks Actor (preserves data); delete from Foundry deletes Chronicle entity
138+
- **Limitation**: Adapters are hardcoded per system. See F-4.5 for generic/dynamic adapter plan.
139+
140+
### F-4.5: Generic System Adapter & Dynamic System Matching (PLANNED)
141+
- **Goal**: Any game system (built-in or user-uploaded custom) works with character sync out of the box, with no Foundry module code changes required.
142+
- **Remove `SYSTEM_MAP` hardcoding**: Instead of mapping Foundry `game.system.id` → Chronicle system ID via a static JS object, query `GET /systems` and match by `foundry_system_id` field on the system manifest. Systems API already returns all registered systems. Add `foundry_system_id` to system manifest schema so custom uploads can declare their Foundry compatibility.
143+
- **Generic adapter**: New `adapters/generic-adapter.mjs` that reads character field definitions from the Chronicle API (`GET /systems/:id/character-fields` or from the character preset in the entity types response). For each field in the preset, it generates `toChronicleFields()` mappings (Foundry `system.*` → Chronicle `fields_data.*`) and `fromChronicleFields()` reverse mappings.
144+
- **Field mapping convention**: The generic adapter uses a convention-based approach: Chronicle field keys map to Foundry `system.<path>` using a `foundry_path` property on each field definition. Systems that declare `foundry_path` on their character preset fields get automatic bidirectional sync. Fields without `foundry_path` are pushed to Chronicle (read from Foundry's flat actor data) but not written back.
145+
- **Fallback to specific adapters**: dnd5e and pf2e adapters remain as overrides for known systems (they handle edge cases like PF2e's derived values). Generic adapter is the fallback for any system not in the override list.
146+
- **Server changes**: Add optional `foundry_system_id` and `foundry_path` fields to system manifest `entity_presets[].fields[]`. Expose character preset fields via API so the Foundry module can read them. Custom system upload already validates manifests, so these fields would be available on upload.
147+
- **Custom system workflow**: User uploads a custom game system ZIP to Chronicle with a manifest that includes `foundry_system_id: "my-system"` and character preset fields with `foundry_path` annotations. Foundry module auto-detects the system, reads field definitions from API, and syncs characters without any adapter code.
148+
149+
### F-5: NPC Viewer / Hall (website feature)
150+
- Campaign route `/campaigns/:id/npcs` — gallery of revealed NPCs
151+
- Foundry integration: ownership change on NPC journal → auto-reveal on Chronicle
152+
153+
### F-6: Armory / Inventory System
154+
- Items with game-mechanic fields, character "Inventory" tab via relations
155+
- Foundry sync: Actor inventory ↔ Chronicle inventory relations
156+
157+
### F-7: Shop / Marketplace Enhancement
158+
- Transaction logging, currency tracking, stock management
159+
- Foundry: purchase → update character inventory on both sides

0 commit comments

Comments
 (0)