Skip to content

Summer Patch Going Live - #32

Open
codeGlaze wants to merge 1671 commits into
developfrom
integration
Open

codeGlaze wants to merge 1671 commits into
developfrom
integration

Conversation

@codeGlaze

@codeGlaze codeGlaze commented Sep 6, 2026 •

Copy link
Copy Markdown
Owner

Takes integration into develop for the Summer Patch. What it does for players and admins is in CHANGELOG.md, under [Summer Patch].

Review: the PRs feeding this were reviewed one at a time, up to #79 (fix/release-cleanup). This PR is too big for CodeRabbit to stand in for them.

Before the upstream PR: remove the fork-only files .coderabbit.yaml, .github/workflows/agent-trailers.yml and .github/workflows/branch-shape.yml.

Upstream issues this release fixes

Found by reading every open Orcpub/orcpub issue against this code on 2026-10-04; each has its evidence on the OrcPub Tracker. They close when Summer Patch merges upstream.

Branches merged into integration without a PR (2026-08-27 to 2026-09-18)

Listed so each change has a trail. Each is a first-parent merge in git log origin/integration.

  • feat/starting-equipment (2026-08-31), feature/one-template-per-style (09-05)
  • perf/homebrew-builder-loop, feature/browser-side-character-images, feat/whats-new-panel (09-06)
  • fix/header-flyout-under-sticky-toolbar, fix/tall-flyout-and-held-release, fix/release-panel-hold-ceiling, hotfix/my-content-dropdown-and-probe-cost, hotfix/boot-config-report, test/overlay-probe-user-menu-and-modals (09-07)
  • fix/message-banner-spacing (09-08)
  • fix/homebrew-save-keys, fix/filtered-list-staleness (09-13)
  • fix/get-auth-token-undeclared, feat/source-tagged-keys (09-18)

Housekeeping: upstream issues already resolved elsewhere

These are already fixed upstream, duplicated, or cannot occur in current code; they can be closed with these references.

Issue Resolved by
Orcpub#27 PR Orcpub#26 (729225cd), Apache repository mirrored over https
Orcpub#86 commit 20d613a7 (2018), race builder offers a language choice including homebrew languages
Orcpub#524 PR Orcpub#648 (2539a0c1), Missing Content warning; this release widens it to spells and languages
Orcpub#612 commit 6b2e295c (2017), Champion adds half proficiency to initiative
Orcpub#659 PR Orcpub#641 (423b7632, ab95ba77), custom weapon melee/ranged reset
Orcpub#130 duplicate of Orcpub#108
Orcpub#179 duplicate of Orcpub#178
Orcpub#554 duplicate of Orcpub#340
Orcpub#182 not reproducible: Half-Elf has no subrace choice now
Orcpub#312 not reproducible: working as the rules describe, as the maintainers agreed
Orcpub#399 not reproducible: the Brave pop-up it describes is gone from the code
Orcpub#437 not reproducible: working as the rules describe, as the maintainers agreed

codeGlaze added a commit that referenced this pull request Sep 27, 2026
…lt here

The owner has paused the agent on PR #34 so that this session builds the one
authoritative fix for homebrew keys. The work branch is port/save-gate, cut from
integration (a0d9e1d) and now pushed; #34's homebrew commits are inputs to the
fix, not a competing version.

plan-next.md §0a becomes that fix: three inputs (integration's key model, this
branch's save gate, #34's load/import repair and link-stranding probes) merged
into one model rather than stacked; the scope widened to everything that points
at a key -- including other library items, the side the save rework never
touched and where #34 had got to; owner checkpoints after the map and after the
design; and the landing path through integration, #32 to develop, and #34
pulling integration and dropping what this supersedes. The stopgap question is
resolved: nothing lands on its own.

BRANCH.md carries the ownership, the branch table and the three open PRs that
touch this, and states that this branch's own purpose is still grants.

Also corrects a core fact committed two days ago: "homebrew never touches the
server" is true of the library only. Custom content entered on a character
travels with the character, and custom magic items are server-backed.
@codeGlaze codeGlaze changed the title docs: calibrate the hyphen finding between the alarm and the dismissal Promote integration to develop (Summer Patch) Oct 4, 2026
@codeGlaze codeGlaze changed the title Promote integration to develop (Summer Patch) Summer Patch Going Live Oct 4, 2026
Comment thread scripts/test/trace-probe.js Fixed
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Comment thread test/browser/soak_e2e.js Fixed
The card's width already left a 16px gutter, but its padding was added on top, so it ran 12px
past the right edge of a 390px screen. srd_reference_e2e now checks the card stays on screen.
A5 was added because of a misreading: the question was which A size is
closest to Letter, and the answer is A4. Letter is the default and A4 the
second layout to design around; a half sheet is a pamphlet format nobody
asked for.

It cost more than it was worth. It needed sheet-page-sizes and a gate in
fit-sheet-to-page!, because re-papering a 612x792 template onto A5 cut off
159 of its 335 fields. It produced the A5-only running-head overflow. And an
A5 export mixed paper sizes in one file.

valid-page-sizes is #{:letter :a4}. A request for :a5 now falls back to
Letter like any unknown option, rather than erroring. sheet-page-sizes and
its gate go: every offered paper now fits the sheet. The clipping test
iterates draw/page-sizes, so a paper added later that cuts the sheet fails
it.

No baseline moved; there was never an A5 baseline.
normalize-text treated WinAnsi as Unicode U+0000-U+00FF and refused the
rest. WinAnsi is a byte encoding: its 0x80-0x9F slots hold the curly
quotes, en and em dash, ellipsis, bullet, oe, OE and the euro sign, and
Helvetica draws all of them (asserted in winansi_test, not assumed).

So the sheet flattened punctuation and printed French oe as `?`, and the
spellbook did the same to 135 characters of SRD text in a font that draws
them. The earlier coverage table blamed Helvetica for the French loss; it
was this.

winansi? reads PDFBox's own WinAnsiEncoding table, so it is not a
hand-maintained list. normalize-text keeps a character when keep? says the
font draws it, falls back to the old ASCII substitutions only when it
cannot, and prints `?` last. C1 controls (U+0080-U+009F), which it used to
pass through to a font with no glyph for them, are now refused.

The sheet's per-field switch now changes font only when the other font
refuses FEWER characters. Before, a value whose one odd character neither
font has -- CJK beside Latin text -- switched font and gained nothing.

Two tests pinned the bug ("smart quotes/dash downgraded"); reversed, with
the reason recorded in place. Spellbook baselines re-seeded: 6 pages per
paper moved, ONLY their :sha and :text -- operator counts, geometry and
colour are unchanged, so characters changed and layout did not.

1121 tests, 7413 assertions green under the default locale, tr_TR and
es_ES. Lint 0 errors.
The per-field switch printed those fields in Vollkorn, a serif, beside
Helvetica neighbours. Compared side by side, rendered from the sheet:

  Vollkorn         prints, but serif among sans       505,744 bytes
  Nimbus Sans      indistinguishable from Helvetica   388,172 bytes
  FreeSans         close, looser leading            1,104,746 bytes
  TeX Gyre Heros   PDFBox cannot embed CFF outlines

Nimbus Sans is URW's metric clone of Helvetica: all 190 Latin-1 advance
widths are identical to the AFM's, so a switched field fits exactly as it
would have in Helvetica. It covers Polish, Turkish, Czech, Cyrillic and
Greek. It has no Vietnamese, which prints `?` on the sheet; the spellbook
and cards still print it, in Vollkorn.

It lives in resources/fonts, not resources/public. Its licence is AGPL-3.0
with an exception for embedding in a PDF, which does not extend to serving
the file. Licence text and provenance (source URL, SHA-256) are alongside
it in resources/fonts/README.md.

A test pins the width match, so swapping in a lookalike that is not
metric-compatible fails rather than silently changing how fields fit.

Latin-1 sheets keep Helvetica; no baseline moved. 1123 tests, 7416
assertions green under the default locale, tr_TR and es_ES. Lint 0 errors.
Two findings from review, both measured before fixing.

svg-path-ops passed every arc to arc-points, which divides by the radius
and by the chord. A zero radius or an arc ending where it starts came back
as 6 NaN ops of 7, written straight into the PDF content stream. SVG 1.1
appendix F.6.2 says: a zero radius is a straight line to the endpoint, and
an arc to its own start draws nothing. Both handled before sampling. No
vendored icon has one, but the function takes arbitrary path data.

dev/user.clj create-user! and email-exists? lowercased email with
str/lower-case. They serve the CLI and REPL, which never start the server,
so pin-locale! never runs there: on a Turkish machine IVAN@x.com would be
stored and looked up as ıvan@x.com. Now ascii-lower-case, and dev/user.clj
joins identity-files, so the guard covers it.

One new lint warning, accepted: dev/style_gallery.clj re-requires
orcpub.common, now also required by user.clj, the namespace the script
runs in. It re-requires orcpub.pdf and clojure.java.io for the same
reason, so it still runs without the dev profile. Removing the line would
break that to save a warning.

1124 tests, 7421 assertions green under the default locale, tr_TR and
es_ES. Lint 0 errors. No baseline moved.
The sheet mixed two fonts: Helvetica for fields it could draw, Nimbus Sans
for the rest. That split existed only to keep Latin-1 exports
byte-identical. It is not worth two fonts on one form, so the switch is
reversed. Every text field, filled or blank, names /NmS -- a blank field
typed into in a viewer gets Nimbus's glyphs too. Embedding also ends the
viewer lottery: standard-14 Helvetica is never embedded, so each viewer
drew its own substitute.

Cost, measured on a Latin-only sheet: 290,018 -> 393,146 bytes, +101 KB,
the full embed a fillable form needs.

The trap, found by an existing test. PDVariableText.setDefaultAppearance
copies the field's DA over every kid widget's own DA (PDFBOX-5797). Style 1
sets fixed sizes on 25 widgets -- 24 at 8pt, one at 10pt -- and swapping
the font through it turned them into auto-size: a save box holding +7 went
from 8pt to 4.89pt. The previous commit had the same fault for any
non-Latin value in one of those 25. use-font! now rewrites each
dictionary's DA directly, changing only the font name. 112 merged
field/widget dictionaries that only inherited a DA now carry their own,
still auto-size.

The sheet baselines could not catch this: they pin page artwork and widget
rectangles, not field appearances. A new test pins every widget's
template size.

drawn-text in pdf_test read appearance bytes as text. With a Type0 font
those are glyph codes, so a drawn "4" read as nothing; it now decodes
through the font. The refused-count helper is gone with the switch it
served.

1125 tests, 7425 assertions green under the default locale, tr_TR and
es_ES. Lint 0 errors. No baseline moved.
…P message

Registration mode used to print at namespace load from routes.clj, separately from
the startup report -- in every REPL and test run, and nowhere near the rest of the
effective configuration. It is now an ALLOW_UNVERIFIED_REGISTRATION row in the
report, whose value is the combination of that flag and EMAIL_SERVER_URL:

  verified by email                       SMTP set
  REFUSED: no mail server                 no SMTP, no opt-in
  OPEN: no address check                  no SMTP, ALLOW_UNVERIFIED_REGISTRATION=true
  verified by email (flag set, inert)     both set: harmless today, but signup opens
                                          without checks if the SMTP value is lost

The routes.clj printout is removed. The report only prints when the server starts,
which is where this belongs.

Also corrects email-missing-message, the (!!) block printed when EMAIL_SERVER_URL is
missing. It still said signup throws inside the transaction and tells the user to
try again -- the bug the registration changes fixed. It now says password reset
fails (still true; EMAIL_SERVER_URL stays critical for that), that signup is
refused unless the opt-in is set, and points at the row that says which. The same
outdated story was in a test description in config_report_test.clj and is fixed
there too, and docs/email-system.md described the removed startup warnings.

Tests pin all four modes, the row's presence in the rendered banner, and that the
message no longer describes the old behaviour; against integration's source they
fail with "No such var: config/registration-mode".

lein test 1064 tests / 7039 assertions / 0 failures. lein lint errors 0, warnings
30 -- one fewer than integration's, none in these files.
…uses

The schema stores used uses per reset period, and dawn and short rest were missing, so saving a
character that had used a once-per-dawn weapon power failed. features_used_save_test saves a use
under every period a weapon's recharge can name.
…onger fails a check

A ctrl-click to-hit on desktop opens a card with both dice, the unused one struck. The check on
the damage options read 'Damage' first, which a real natural 20 (1 run in 20) labels 'Critical
damage'; it now accepts either.
… deleted once sent

Sign-up verification, password reset, the password-changed, email-change and sign-in notices,
and the artist emails are written to the database in the same transaction as the action that
causes them. One worker sends them in the background, retries a busy or down mail server with
growing gaps, gives up on a permanent refusal or after 8 attempts with an admin alert, and deletes
each email once sent; the message keeps no history, since it holds reset and verification links.
Claims are compare-and-set, so several servers never send the same email.

Signing up while the mail server is down now works and the email follows, so the rollback that
undid an account when its email failed is gone. A notice that cannot be built never blocks the
change it reports on. The character-load report still sends directly, since the page reports
the result to the player.
… deleted once sent

The previous commit holds only the test file's rename; this one is the change.

Sign-up verification, password reset, the password-changed, email-change and sign-in notices,
and the artist emails are written to the database in the same transaction as the action that
causes them. One worker sends them in the background, retries a busy or down mail server with
growing gaps, gives up on a permanent refusal or after 8 attempts with an admin alert, and deletes
each email once sent; the message keeps no history, since it holds reset and verification links.
Claims are compare-and-set, so several servers never send the same email.

Signing up while the mail server is down now works and the email follows, so the rollback that
undid an account when its email failed is gone. A notice that cannot be built never blocks the
change it reports on. The character-load report still sends directly, since the page reports
the result to the player.
…hes Java

Docker logs are capped per container (LOG_MAX_SIZE, LOG_MAX_FILES), so they cannot fill the disk.
Java starts with flags that make it exit cleanly when it runs out of memory, writing a heap dump,
and write a crash log if it dies; both go to the new orcpub-state volume with the unsent admin
alerts, so they survive the container being replaced. On the next start the server emails the
admin, once per file, what happened and where the file is.

JAVA_OPTS was passed to the container but `java -jar` never read it, so the documented memory
settings were ignored and the app ran with 25% of its container's memory. It now reaches Java
through JDK_JAVA_OPTIONS, after the crash flags, in Compose and in generated Swarm stacks.
Characterization: the export's own drawing (headings, annotations, relabels,
site line, notice page, cards) uses three Standard 14 Helvetica faces, none
embedded. Read from a real export through generate-character-pdf.
The export's own drawing (spell-column headings, relabelled numerals,
annotations, site line, notice page, card captions) used three Standard 14
Helvetica faces, none embedded: the viewer substituted its own, and they are
WinAnsi-only, so a heading for "Czarodziej Lodz" with Polish letters printed
"Czarodziej ?od?". Now Nimbus Sans Regular, Bold and Bold Italic (URW, OFL-
compatible AGPL + PDF-embedding exception), the face the form fields already
use.

- draw/load-sans: per-document map of lazily embedded faces; load-fonts
  includes it. The export makes one and passes it everywhere.
- Each face's 224 WinAnsi widths equal its Helvetica's (pinned by
  nimbus-sans-is-helvetica-width), so no position moved: the card and sheet
  baselines differ only in text strings (564+564+63 lines) and text hashes.
- fit-text measures in the fields' own font; it measured Helvetica, which
  counts L-stroke as "?".
- site-line-pages scans for artwork site lines before anything is drawn.
  Scanning after drawing read not-yet-embedded fonts and logged a fallback
  warning on style 4 exports. A saved export now renders with none.
- Tests that read or render text use a saved copy (pdf-ops/reloaded).

Size: plain sheet +3.2 KB (0.9%), sheet with cards +15.2 KB (3.5%).
Artist pages: a public page for each portrait artist
Weapons table, item charges, the Rest box and casting from items
…ith no name is still offered

subclass-option left the option key to the name, so a subclass whose name does not make its
stored key (a short name, or none) was offered under another key (or none) and could never be
picked in the character builder.
Seen failing with the fix reverted (options.cljc at 097f983), lein test :only orcpub.dnd.e5.subclass-key-test:
  FAIL a name that does not make the stored key: (not (= :my-oath :x))
  FAIL no name at all: (not (= :nameless nil))
  Ran 1 tests containing 3 assertions. 2 failures, 0 errors.
The name-derived-key claim passes either way. With the fix: 0 failures, 0 errors.
…nster scores, lone ability increase, shared reports, stale set-aside, shared count, auto-clean, pre-fix copy, audit exit)
…bility increase, shared reports, replaced entries

Fail first, with the fixes reverted: clj shape table 3 failures + 2 errors (leading zeros, monster score, lone ability increase); cljs suite 3 failures, only a-report-from-shared-content-never-sets-aside-a-local-entry and an-entry-replaced-since-its-report-is-not-set-aside.
Guard homebrew: one shape check on every way in, and broken items set aside
…d a full regex escape

The 12 test/e2e scripts each carried an inline copy of the static server, flagged
as js/path-injection by CodeQL (the root-or-inside check was one compound condition).
They now share test/e2e/static-server.js: root serves the host page, a path outside the
root, with a NUL byte or with bad %-encoding gets a 404 before any file read.
soak_e2e.js escapes every regex metacharacter in the section name (js/incomplete-sanitization).

Checked by hand: root, asset, missing route, ../ traversal, bad encoding and NUL. CodeQL
cannot run locally; the alerts clear when it re-scans after this reaches integration.
…port

Browser scripts: fix the 13 CodeQL alerts (path injection, incomplete escape)
Seen failing before it passed: with the missing-file check, the age comparison and the
build-config input each broken in stale_bundle.clj, 7 of the new test's 10 assertions failed
(current-build, missing-build-files x3, newer-client-source, newer-style-source,
newer-build-config); restored, 12 assertions pass. The banner's dev-mode gate is covered with
redefs, not by a break.
codeGlaze added a commit that referenced this pull request Nov 1, 2026
They are on agents/develop now, under docs/archive and scripts/research. CodeRabbit's docs/design
exclusion goes with them: it kept #65 under the 100-file limit by hiding them.
…banner

./menu start and ./menu start server never built anything, so a pull left the page on old code and
the banner up. They now run the rebuild check scripts/e2e/run.sh already had, moved to
scripts/bundle-check.sh, and skip the compile when nothing changed.

Seen failing before it passed. With the out-of-date check removed:
  FAIL a changed source builds the client: calls '' exit 0
  FAIL a changed build config builds the client: calls '' exit 0
  FAIL no bundle builds the client: calls '' exit 0
  start build: 4 passed, 8 failed
With a failed build ignored:
  FAIL a failed build stops the start
  start build: 9 passed, 3 failed
Restored: start build: 12 passed, 0 failed
Reads DEV_MODE as the app does (only "true", unset is dev), counts project.clj as a build input,
skips a build only for a real Figwheel or Garden process, refuses a production start under a dev
Figwheel, stops the Datomic it started when a build fails, and runs the PR check on a retargeted PR.

Seen failing before it passed, with the reviewed bundle-check.sh back:
  FAIL Figwheel here while production is wanted stops the start
  FAIL another program on the port here does not skip the build
  FAIL a changed project.clj builds the client
  FAIL DEV_MODE unset means dev
  FAIL TRUE counts as dev
  FAIL 1 is not dev, as the app reads it
  FAIL a reused garden.pid does not skip the stylesheet
  start build: 13 passed, 7 failed
Restored: start build: 20 passed, 0 failed
Release cleanup: unblock #32, stray files off the code line, menu builds what is stale
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants