Skip to content

Write full GPL citation styles and locales from the style manuals - #103

Merged
dknauss merged 1 commit into
mainfrom
claude/keen-tesla-49orwb
Sep 26, 2026
Merged

dknauss merged 1 commit into
mainfrom
claude/keen-tesla-49orwb

Conversation

@dknauss

@dknauss dknauss commented Sep 26, 2026

Copy link
Copy Markdown
Owner

Summary

What changed?

The nine bundled CSL styles and three locales are rewritten in full, each from its own manual:

Style Source
Chicago notes-bibliography Chicago Manual of Style, 17th ed.
Chicago author-date Chicago Manual of Style, 17th ed.
APA 7 APA Publication Manual, 7th ed.
MLA 9 MLA Handbook, 9th ed.
Harvard Cite Them Right
IEEE IEEE Reference Guide (2023)
Vancouver NLM Citing Medicine
OSCOLA OSCOLA, 4th ed.
ABNT NBR 6023:2018

Before and after, for one journal article:

Style Before After
Chicago NB Green, S. Metadata Drift in Institutional Repositories. Journal of Library Systems. 2020. https://doi.org/… Green, Samuel. “Metadata Drift in Institutional Repositories.” Journal of Library Systems 27, no. 2 (April 2020): 97–111. https://doi.org/…
APA 7 Green, S. 2020. … Green, S. (2020). Metadata Drift in Institutional Repositories. Journal of Library Systems, 27(2), 97–111. https://doi.org/…
OSCOLA (same shape as Chicago) Green S, ‘Metadata Drift in Institutional Repositories’ (2020) 27 Journal of Library Systems 97
ABNT (English terms) GREEN, Samuel. Metadata Drift in Institutional Repositories. Journal of Library Systems, v. 27, n. 2, p. 97-111, abr. 2020. DOI: https://doi.org/…

The full reviewed output for all 20 corpus items in every style is in tests/fixtures/csl-styles/*.txt.

Why were the styles stand-ins? The official CSL styles and locales are CC BY-SA 3.0. That isn't GPL-compatible, and SPEC.md and THIRD-PARTY-NOTICES.txt forbid bundling them. So these are written for Borges from each manual's rules, not copied from the CSL files, and stay GPL-2.0-or-later.

  • Before: about 2 KB stand-ins. Every given name was initialized. Volume, issue, pages, editors, translators, editions, and access dates were dropped. The locales had no month names or quotation marks, and pt-BR was an English copy, so ABNT printed "and" and "accessed".
  • Now: each manual's author-list rules apply ("and" or "&", "et al." thresholds, APA's 21+ ellipsis). So do its forms for journals, chapters, edited and translated books, magazines and newspapers, webpages, theses, reports, conference papers, preprints, cases, and statutes. Harvard now uses en-GB.

Formatter changes. These work around citeproc-php 2.7 behavior; each quirk is in docs/external-eccentricities.md.

  • One CiteProc per entry. Name state leaked between entries: once one entry was cut to "et al.", every later entry lost its "and". This bug was live before this PR.
  • bibliography_builder_prepare_csl_for_formatter():
    • maps literal names to family-only names. Organization authors rendered empty, which was also live before this PR;
    • derives page-first for OSCOLA.
  • Cleanup in bibliography_builder_normalize_formatted_text():
    • removes the " ," citeproc-php puts before labels;
    • initializes hyphenated names fully ("J.-W.", not "J.- woo");
    • moves , and . inside closing quotes for en-US styles.

Deliberate deviations, listed in docs/csl-styles.md:

  • Titles keep their stored capitalization, because the block re-applies italics by matching the title text.
  • URLs and DOIs stay plain https:// links, so OSCOLA drops its angle brackets.
  • There is no Chicago 3-em dash for repeated authors.
  • Journal titles are not abbreviated.

Existing posts keep their saved text until an entry is added or edited or the style is changed. The editor never reformats on load.

Heads-up for the planned year suffixes (2020a/2020b, REQ-S4). Formatting each entry on its own means citeproc can't see the whole list. When that feature lands, it will need to assign suffixes itself; the plan already coordinates suffixes with the JS sorter. SortCoordinationTest still passes, because the styles keep <sort> blocks, now in separate sort-* macros.

Validation

  • composer test:php passes (287). It includes:
    • the new CslStyleGoldenTest, which pins 9 styles × 20 items and fails if the vendor/ copy is stale;
    • the new FormatterAdaptationTest (7 tests).
  • composer lint:php and composer analyze:php pass.
  • npm run lint:js and npm run test (872) pass. No JS changed.
  • verify-metrics.sh passes. The LOC rows are updated.
  • Tested in the block editor. I haven't loaded a live editor yet. save()'s italics and links rely on the title and URL text, which the styles keep as stored.

Checklist

  • Linked issue or explained why none was needed: requested directly (replace the stand-in styles).
  • No sensitive details disclosed publicly

🤖 Generated with Claude Code

https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2


Generated by Claude Code

The nine bundled CSL styles were ~2 KB stand-ins: every given name
initialized; no volume, issue, pages, editors, translators, editions,
or access dates. The official CSL styles are CC BY-SA 3.0, which the
repo's license policy (SPEC.md, THIRD-PARTY-NOTICES.txt) and
WordPress.org rule out, so each style is written for Borges from its
manual and stays GPL-2.0-or-later: Chicago notes-bibliography and
author-date (17th), APA 7, MLA 9, Harvard (Cite Them Right), IEEE,
Vancouver (Citing Medicine), OSCOLA, and ABNT NBR 6023.

The en-US, en-GB, and pt-BR locales gain month names, quotation
marks, and role terms; pt-BR was an English copy, so ABNT printed
English. Harvard now formats with en-GB.

citeproc-php 2.7 workarounds in the formatter:
- one CiteProc instance per entry (name state leaked across entries:
  after an "et al." entry, later entries lost their "and");
- literal names mapped to family-only names (they rendered empty, so
  organization authors vanished);
- page-first derived for OSCOLA;
- cleanup of " ," before labels, hyphenated initials ("J.- woo"),
  and commas/periods inside closing quotes for en-US styles.
Style-side conventions and every quirk are in docs/csl-styles.md and
docs/external-eccentricities.md. Sort keys moved to separate macros so
the sort-coordination tests still pass without disturbing name state.

Tests: CslStyleGoldenTest pins every style's output for a 20-item
corpus (and fails on a stale vendor/ copy); FormatterAdaptationTest
covers the workarounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2
@codecov

codecov Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.75%. Comparing base (cd4a381) to head (1522f1e).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #103      +/-   ##
==========================================
+ Coverage   86.65%   86.75%   +0.09%     
==========================================
  Files          52       52              
  Lines        5367     5406      +39     
  Branches      597      597              
==========================================
+ Hits         4651     4690      +39     
  Misses        235      235              
  Partials      481      481              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dknauss
dknauss marked this pull request as ready for review September 26, 2026 17:18
@dknauss
dknauss merged commit 7fb1372 into main Sep 26, 2026
16 checks passed
@dknauss
dknauss deleted the claude/keen-tesla-49orwb branch September 26, 2026 17:18
dknauss pushed a commit that referenced this pull request Sep 26, 2026
Full GPL CSL styles and locales (#103), Phase 05 M0-M3 (stable IDs,
review routes and abilities, opt-in citation and block write routes:
#104, #106), the save-markup language fix, reference-manager .bib
fixes, the Playground demo page (#102), and the runtime matrix on the
release package with formatter-parity and write-route checks (#105).

Version bumped in the plugin header, block.json, readme.txt Stable tag,
the POT header, and the package manifests. CHANGELOG [Unreleased] is
dated as 1.7.0; readme.txt gains a user-facing changelog entry, an
Upgrade Notice, and an FAQ note on the opt-in write routes. README
highlights and STATE/ROADMAP move to the 1.7.0 baseline.

Tested up to stays 7.1: WordPress latest is 7.1.2, which CI's runtime
matrix exercises, including formatter parity across all nine styles.

package-release.sh now also prunes .git directories from vendor/. When
Composer falls back from dist to a git clone (as it did in this
sandbox), the ZIP otherwise carried 4 MB of git metadata.

Release checklist gates run locally: lint:js, lint:css, lint:i18n,
npm audit (0), composer audit (none), Jest 873 passed / 2 skipped,
PHPUnit 318 OK (the 4 vendor deprecations already on main), build,
package:release (552,718-byte ZIP). citeproc-php v2.7.1 is current.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2
@dknauss dknauss mentioned this pull request Sep 26, 2026
9 tasks done
dknauss added a commit that referenced this pull request Sep 26, 2026
* chore(release): 1.7.0

Full GPL CSL styles and locales (#103), Phase 05 M0-M3 (stable IDs,
review routes and abilities, opt-in citation and block write routes:
#104, #106), the save-markup language fix, reference-manager .bib
fixes, the Playground demo page (#102), and the runtime matrix on the
release package with formatter-parity and write-route checks (#105).

Version bumped in the plugin header, block.json, readme.txt Stable tag,
the POT header, and the package manifests. CHANGELOG [Unreleased] is
dated as 1.7.0; readme.txt gains a user-facing changelog entry, an
Upgrade Notice, and an FAQ note on the opt-in write routes. README
highlights and STATE/ROADMAP move to the 1.7.0 baseline.

Tested up to stays 7.1: WordPress latest is 7.1.2, which CI's runtime
matrix exercises, including formatter parity across all nine styles.

package-release.sh now also prunes .git directories from vendor/. When
Composer falls back from dist to a git clone (as it did in this
sandbox), the ZIP otherwise carried 4 MB of git metadata.

Release checklist gates run locally: lint:js, lint:css, lint:i18n,
npm audit (0), composer audit (none), Jest 873 passed / 2 skipped,
PHPUnit 318 OK (the 4 vendor deprecations already on main), build,
package:release (552,718-byte ZIP). citeproc-php v2.7.1 is current.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2

* Parse each CSL style once per format request

CiteProc::render() calls init() every time, which builds a new Context,
reads and parses the locale file, and rebuilds the style's object tree:
about 3 ms, against well under 1 ms to render an entry. With one render
per entry (the fix that stopped an "et al." entry from dropping the
next entry's "and"), 50 entries took about 90 ms, against about 8 ms in
1.6.0 with its short stand-in styles.

Bibliography_Builder_Reusable_CiteProc parses once, records every
property of every object in the parsed style tree, and before each later
render restores them, resets the Context's per-render lists, and zeroes
Layout's static cited-item counter. Each entry renders against the state
a fresh parse would give it: 50 entries now take about 25 ms.

The nine style goldens still match byte for byte. A new test formats the
corpus in reverse order, putting the "et al." and APA 21+ entries ahead
of the short author lists, and requires every entry to match its golden
line; with the restore disabled, both the goldens and the new test fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2

* Drop the #[\Override] attribute from the reusable formatter

PHP 7.4 reads an attribute on its own line as a comment (php -l on
7.4 passes, and so does the PHP 7.4 runtime smoke), but Copilot flagged
it as a 7.4 parse error, and compatibility scanners can do the same. It
does nothing at runtime, so drop it, and turn off Psalm's
MissingOverrideAttribute, which cannot apply to a plugin that supports
PHP 7.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants