Skip to content

v0.4.0: Foundry v13/v14 rewrite — release to main - #4

Merged
snorith merged 23 commits into
mainfrom
retire-legacy-src
Aug 9, 2026
Merged

v0.4.0: Foundry v13/v14 rewrite — release to main#4
snorith merged 23 commits into
mainfrom
retire-legacy-src

Conversation

@snorith

@snorith snorith commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

Brings the complete v13/v14 rewrite (merged to develop in #3) to main, plus the final retirement step:

  • Legacy src/ removed — the 0.7-era source served as the rewrite's behavioral spec and lives on in git history. src/system.json stays as the tombstone manifest: legacy installs poll raw.githubusercontent.com/snorith/hellas/master/src/system.json (served from main via the branch-rename redirect), and the tombstone points them at the new GitHub-Releases channel with dual legacy/modern keys.
  • z/ working docs dropped (plan, spec, review ledgers — all in history), along with the one-time NeDB→JSON converter whose input no longer exists.
  • CLAUDE.md rewritten for the current architecture; stale .gitignore entries cleaned.

Merging this makes the tombstone live for legacy update channels. It does NOT publish a release — that happens on the v0.4.0 tag push, ideally after the remaining manual checklist items (rolls, legacy-world migration) are done.

Verification

Static audit + migration smoke tests green after the removal. (Pack compilation is verified in CI at release time; local LevelDB rebuilds require Foundry to be closed — single-writer lock.)

🤖 Generated with Claude Code

snorith and others added 23 commits August 8, 2026 18:21
Adds CLAUDE.md with repository overview, build commands, architecture details, and coding conventions for AI assistant context. References it via AGENTS.md.
Ground-up rebuild against current Foundry idioms, using the legacy
0.7-era code as the behavioral spec (extracted to z/SPEC.md):

- Zero build steps: repo root is the system directory; plain JS ES
  modules + JSDoc, plain CSS with native nesting. Old gulp/rollup/
  TypeScript/Tailwind toolchain and committed release zips removed.
- Modern manifest: id/compatibility {13.347 -> 14}, documentTypes for
  character + 5 item types; template.json replaced by TypeDataModel
  schemas with presence-guarded migrateData (node smoke tests in
  tools/test-migrations.mjs).
- Documents & rolls: HellasActor/HellasItem replace the legacy Proxy
  polymorphism; async dice; DialogV2 modifier prompt; omega-table
  outcomes; skill names synced in _preCreate/_preUpdate instead of
  writes during prepareData.
- Sheets on ApplicationV2 + HandlebarsApplicationMixin: one item sheet
  for all five types, four-tab actor sheet; all actions (incl. rolls)
  gated on isEditable; core data-tooltip-html replaces vendored
  tooltipster; no jQuery, no custom Handlebars helpers.
- Compendia: NeDB journals deduped and converted to per-document JSON
  sources (packs/_source), compiled to LevelDB via foundryvtt-cli
  (tools/build-packs.mjs); document ids preserved.
- 19 legacy bugs fixed rather than ported (z/SPEC.md fix-don't-port
  list), incl. fate points never persisting, sxithborn data-key typo
  (with migration), and DB writes during render/prepare.
- Verification: tools/audit.mjs static cross-reference audit (actions,
  i18n incl. composed key families, form paths vs schemas, template
  paths, packs, manifest) is clean; manual checklist in z/CHECKLIST.md.
- Release: GitHub Actions on v* tag push; tombstone manifest kept at
  src/system.json for the legacy raw-GitHub update channel.

Legacy source stays under src/ as reference until the manual checklist
passes; z/ holds the rewrite plan, spec, and review ledger.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Persist normalized skill selectors in _preCreate/_preUpdate and reset
  the name when skill is cleared (devin D1, major). Normalization stays
  out of migrateData: update deltas lack whole-document context.
- Gate #onItemEdit on isEditable like every other action (devin D2).
- Ledger in z/PLAN.md records all findings incl. rejected codex P1
  (scrollable [""] is the documented v13 idiom) and the verified
  NumberField integer-rounding behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Remove the <form> nested inside DialogV2 content (devin D6, major):
  DialogV2 supplies its own form; per the AppV2 conversion guide a nested
  form risks button.form missing the inputs entirely.
- Number()-parse dialog modifiers so exponent forms survive (codex C2).
- Clear dependent skill selectors when skill is emptied (devin D8).
- Fate label tooltip now uses the purpose-built fatepoints.title key —
  legacy formatted args into the placeholder-less description (devin
  D10, logged as legacy bug F20).
- Ledger updated; D7 recorded as accepted-by-design, D9 rejected with
  reason (core requires a name at Item.create).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both reviewers clean on the rev-2 folds (codex: no regression; devin:
all four folds sound, fresh sweep empty). Review loop terminal state
recorded in the z/PLAN.md ledger; only the execution-gated tombstone
check (D4) remains, carried to the manual checklist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Droid verified the full v13 API surface, data-prep ordering, roll
pipeline, templates, tools, and manifests: no blockers or majors.
Folds: STR-shortfall arithmetic uses one guarded minStr (droid's NaN
scenario disproven by execution — null coerces to 0 — folded for
clarity only); dead || true removed from audit.mjs; three unused
legacy i18n short-name keys carried wrong "Fate Points" values,
corrected. Adds the best-practices comparison report (z/BEST-PRACTICES.md).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…plan

- Ledger rev 5: droid round 2 clean — all three model families converged.
- z/FOUNDRY-BEST-PRACTICES.md: repo-agnostic v13/v14 best-practices
  reference (raw material behind the z/BEST-PRACTICES.md comparison).
- z/phase-9-plan.md: two-phase plan for the best-practices updates
  (9a pre-tag: migration runner, bundled fonts, manifest polish,
  Package Release API; 9b post-release: AE visibility, a11y,
  LOCALIZATION_PREFIXES, enum choices, form helpers).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Remove the 2021-era .nvmrc (node v12); mise.toml now pins node 24 and
the release workflow builds on node 24 to match. All tooling verified
under 24.12.0: migration smoke tests, static audit, pack build and
round-trip. README dev setup mentions mise install.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two plan-review rounds (codex + devin): rev 1 folded recursive:false,
zero-failure stamping + activeGM gate, effects stripping, scope cut of
the unlinked-token pass (delta-override hazard), shell-script secret
guard (resolves a reviewer dispute over Actions env/if semantics),
scripted font fetching, jq -er guards; rev 2 confirmed with mechanical
i18n/checklist alignments. Ledger inside the plan doc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Setup-screen media and world-login background reuse the credited marble
photo; flags.hotReload uses the documented paths-only shape (the
extensions key does not exist in the official flag structure — verified
against the configuration article, resolving the plan's impl-time
check); changelog now points at a real CHANGELOG.md, shipped in the
release zip. Audit verifies media/background paths exist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Roboto (variable woff2 covering weights 300-500 — Google serves one
identical file per weight, verified by checksum) and Caesar Dressing
now ship in fonts/ with their OFL license texts, fetched reproducibly
by tools/fetch-fonts.mjs (magic-byte + size verified). Offline/LAN
installs keep the full visual identity and clients no longer call a
third-party CDN. Synthetic bold remains, matching what the legacy
import produced. README credits the fonts; release zip ships fonts/;
audit verifies the files exist and no googleapis reference remains.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
module/migrations/world.mjs: version-gated blanket re-save of world
actors and items so migrateData's in-memory normalizations (sxithborn
rename, blank-string -> null) finally reach the DB. Runs once per
NEEDS_MIGRATION_VERSION from the single active GM, stamps the
systemMigrationVersion world setting only after a zero-failure pass
(failures retry next launch; re-saving is idempotent). toObject(true)
+ {diff:false, recursive:false} keeps derived values out of the DB and
drops removed legacy keys; item/actor effects payloads stripped;
unlinked-token deltas and compendia deliberately out of scope (see
file header). SYSTEM_ID moved to module/config.mjs to keep the module
importable in bare node (7/7 smoke tests incl. the version gate).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POSTs each release to foundryvtt.com per the official Package Release
API (version-specific manifest asset URL, compatibility read from the
stamped system.json via jq -er guards, maximum:"" as in the official
example). Shell-level secret guard skips cleanly until the package is
registered and FOUNDRY_PACKAGE_TOKEN exists. Body construction
dry-verified locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restore the required hotReload extensions key (codex P2 — the core
design issue foundryvtt#9027 marks extensions required; the
configuration article's paths-only snippet is a simplification, and
dnd5e ships both keys). Harden the fetch-fonts url() regex (devin
nit). Devin's effects-preservation question is reasoned in the ledger
and pinned to a new manual-checklist row for execution confirmation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
en.json carried a dotted-key expansion collision (skill.specifier.title
both a string and a prefix of the stray ...title.label): v13 merges
translations via setProperty, which throws on strict-mode string
property assignment, aborting the ENTIRE language file — every UI
string rendered as its raw key. Stray key removed; the audit now fails
on any expansion collision. Setup-tile media switched to brushed gold
(white marble washed out at tile size).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tables now paint their own light background and ink instead of tinting
whatever core's theme put underneath (dark slabs in dark mode), and the
two-column layout uses minmax(0,1fr) so grid children can shrink
instead of pushing the right column past the window edge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
window-content becomes an overflow-hidden flex column; the active tab
section flexes to the remaining height and scrolls itself. PARTS'
scrollable [""] only preserves scroll positions across re-renders —
the actual overflow behavior is system CSS, which was missing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Core dark-theme headings are pale cream for dark grounds and vanished
on the marble; all headings now take the sheet ink. Default document
icons are white-line SVGs — a dark chip behind .profile-img keeps them
visible while raster portraits cover it entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The input+hint cells become flex rows: the hint text gets its own
flexible column next to the input and wraps within that column.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v13 icon-font checkbox states were illegible on the marble ground
(unchecked rendered as a solid dark block). Suppress core's glyph
pseudo-elements and draw the box + check with our own styles: empty
light box unchecked, dark check when checked, dimmed when disabled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rewrite system from scratch for Foundry VTT v13/v14
The 0.7-era source served as the rewrite's behavioral spec and is now
fully captured by the shipped system and git history. src/system.json
remains as the tombstone manifest for the legacy update channel. The
z/ plan/spec/ledger docs and the one-time NeDB converter (whose input
no longer exists and whose output is committed under packs/_source)
retire with it. CLAUDE.md rewritten for the current architecture;
stale gitignore entry dropped. Audit, smoke tests, and pack round-trip
all green after removal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@snorith
snorith merged commit 4c5bd7f into main Aug 9, 2026
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