Skip to content

Phase 05 M3: block settings and reformat write routes - #106

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?

This adds Tier 3 of the writable REST design. M2 already shipped reorder and ETag, so M3 is these two routes. Both sit behind the same bibliography_builder_enable_write_routes filter and follow M2's rules: edit_post, dry run by default, and If-Match required. They use M2's write path too: the PHP port of save() rebuilds the markup, the change is spliced into only that block, and a check confirms nothing else in the post changed.

  • PATCH /posts/{id}/bibliographies/{ref} changes block settings: headingText, outputJsonLd, outputCoins, outputCslJson, and outputCiteExport.

    • Citations are not touched.
    • An unknown setting or a wrong type gets 400.
    • A setting sent with its block.json default is left out of the block comment, the same way the editor's serializer drops it.
    • citationStyle is refused (400, bibliography_builder_style_needs_reformat), because a style change without reformatting would leave every entry in the old style.
  • POST /posts/{id}/bibliographies/{ref}/reformat with { "style": "<key>" } mirrors the editor's handleCitationStyleChange:

    • it formats every entry in the new style, 50 at a time (the /format limit), up to the editor's 200-citation cap;
    • it keeps manual display text and the BibTeX and BibLaTeX export strings;
    • it stores entries in the new style's display order;
    • a heading still set to the old style's default becomes the new style's default. A PHP copy of the style registry's headingPlaceholder supplies those defaults, and a drift test pins it to the JS registry.
    • The stored CSL-JSON is untouched. Entries the formatter can't use fail the whole request with 409, listed in data.entries.
  • Runtime smoke: every matrix cell now sends a real settings PATCH and a real reformat to APA over HTTP. It then checks:

    • the APA text on the text route;
    • the new heading and COinS on the front end;
    • that the block's markup still matches save().

    The PATCH shares its path with the public GET read route, so this also proves WordPress falls through to it (match_request_to_handler skips a route with no handler for the method).

  • Dev Playground: borgesWrite.settings() and borgesWrite.reformat().

  • Docs: docs/rest-write-routes.md (now "Bibliography write routes"), the README, CLAUDE.md, the CHANGELOG, the design memo (M3 status and how it differs from the sketch), the roadmap, and the metrics (PHP line count).

Why? It was requested ("start m3"), and it's the next milestone in the Phase 05 sequencing table. It also clears the M0–M3 blocker on write abilities (M5).

Validation

  • npm run lint:js passes
  • npm run lint:css: no CSS changes
  • npm run lint:i18n passes (new PHP strings)
  • npm run test passes (873)
  • npm run build: no JS source changes
  • composer test:php (318 tests), composer lint:php, Psalm, and verify-metrics.sh pass.
  • PHPUnit covers:
    • settings, including the default-omission and 400 cases;
    • reformat from IEEE to APA (re-sorted, the other block untouched, markup equal to save());
    • override, export, and heading preservation (Chicago → ABNT → Chicago);
    • a 60-entry reformat across the 50-item chunks;
    • a citation with no ID;
    • bad styles and a 409 for entries it can't format;
    • the heading drift check.
  • Tested in the block editor. The Docker matrix's new steps run in this PR's CI, and the dev blueprint picks up the new helpers from main after merge.

Checklist

  • Linked issue or explained why none was needed: requested directly (Phase 05 M3).
  • No sensitive details disclosed publicly

🤖 Generated with Claude Code

https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2


Generated by Claude Code

Tier 3 of the writable REST design, behind the M2 opt-in filter and with
the same dry-run, edit_post, and If-Match rules:

- PATCH /posts/{id}/bibliographies/{ref} changes block settings: the
  visible heading and the JSON-LD, COinS, CSL-JSON, and Cite / Export
  toggles. citationStyle is refused with a pointer to reformat, and a
  setting sent with its block.json default leaves the block comment, as
  the editor's serializer drops it.
- POST /posts/{id}/bibliographies/{ref}/reformat switches the citation
  style like the editor's handleCitationStyleChange: every entry is
  formatted in the new style (50 at a time, up to 200), manual text and
  export strings are kept, entries are re-sorted, and a heading still set
  to the old style's default becomes the new style's. Entries the
  formatter cannot use fail the request with 409 and are listed.

The runtime smoke now changes settings and reformats over HTTP in every
cell, the dev Playground helper gains settings() and reformat(), and the
docs, memo, and roadmap mark M3 done.

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

❌ Patch coverage is 93.75000% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.39%. Comparing base (ebef50c) to head (aaf7fdc).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
includes/write-routes.php 93.75% 11 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #106      +/-   ##
==========================================
+ Coverage   87.20%   87.39%   +0.18%     
==========================================
  Files          54       54              
  Lines        5847     6021     +174     
  Branches      597      597              
==========================================
+ Hits         5099     5262     +163     
- Misses        267      278      +11     
  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 20:50
@dknauss
dknauss merged commit 43ef645 into main Sep 26, 2026
16 checks passed
@dknauss
dknauss deleted the claude/keen-tesla-49orwb branch September 26, 2026 20:50
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>
dknauss pushed a commit that referenced this pull request Sep 26, 2026
Since #106, all three nginx cells of the full matrix fail at the
settings PATCH with nginx's own "405 Not Allowed": the smoke test sent
REST requests to /?rest_route=, and for a directory request nginx's
index module only hands GET, HEAD, and POST to index.php, so PATCH,
PUT, and DELETE fall through to the static handler. #105's write steps
used only GET and POST, so they passed. Sites are not affected:
WordPress builds plain-permalink REST URLs as index.php?rest_route=,
and pretty permalinks reach index.php through try_files.

Send every smoke REST request to index.php?rest_route= (REST_URL), and
add nginx-php83-wplatest to the pull-request matrix, which had only
Apache cells and so could not catch this before merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KbVuMPN7YTcV27vwsG3LZ2
dknauss added a commit that referenced this pull request Sep 27, 2026
Since #106, all three nginx cells of the full matrix fail at the
settings PATCH with nginx's own "405 Not Allowed": the smoke test sent
REST requests to /?rest_route=, and for a directory request nginx's
index module only hands GET, HEAD, and POST to index.php, so PATCH,
PUT, and DELETE fall through to the static handler. #105's write steps
used only GET and POST, so they passed. Sites are not affected:
WordPress builds plain-permalink REST URLs as index.php?rest_route=,
and pretty permalinks reach index.php through try_files.

Send every smoke REST request to index.php?rest_route= (REST_URL), and
add nginx-php83-wplatest to the pull-request matrix, which had only
Apache cells and so could not catch this before merge.


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