Repository navigation
Store and teach settings in one shape at every scale - #35
Merged
Merged
Conversation
The template locked the framework at its eb1473c split, before the settings branch merged as 8db8d53: options pages that store each field in its own option, number fields held to their min, max and step on both hosts, and an uninstall consent read from its own option only. The core, settings and woocommerce mirrors move to that split; the bootstrap and shared mirrors are unchanged, and no other Composer or npm package has a release to take beyond TypeScript 7, which stays capped. The move forces no change in the template: it passes no consent key, builds no options page, and its one number field still carries its own validator. Assisted-by: Claude Code:claude-opus-5-5
Options::DEFAULTS holds typed values for three of the four settings and none for the greeting, so the seeding step converts each default by hand, each field converts it again, and a setting added after the first release needs seed lines of its own. WooCommerce also stores each tab field's default whenever its own version changes, so a field default that differs from the seeded one wins or loses by which write runs first. These rows expect the defaults in the form each option stores: the first migration stores each DEFAULTS entry as it stands, the greeting's as an empty string, each autoloaded, and the row fails until its option list names every key; each field's default equals its DEFAULTS entry; and a Unit row records a fingerprint of the key set each seeding step stores, failing when the keys change without a step and fingerprint for them. The fingerprint counts each key after the plugin's prefix, which the fill-in renames. The readers' default rows assert the values they read. Assisted-by: Claude Code:claude-opus-5-5
DEFAULTS held typed values that the seeding step and each field converted by hand, and no entry for the greeting, so a setting added later needed seed lines of its own and a field default could drift from the seeded one. DEFAULTS now holds an entry for every option in the form the option stores it, an empty string standing in for the greeting's translated default. The first migration primes the rows and adds each absent default autoloaded in one loop, which a release that adds settings lists again; each field's default reads its DEFAULTS entry; and the boolean and integer readers fall back to their default through the same filter that reads the stored value. Assisted-by: Claude Code:claude-opus-5-5
The greeting's order-count field carries a validator of its own, which rejects an empty submission and words its own message, while the framework now holds a number field to the min and step it declares on both hosts and treats an emptied number as cleared. These rows expect the framework's rules on the field: a count below 1, off the step of 1, not a number or too large to check keeps the stored count and names the bound it missed; an emptied count stores an empty string, reads as the default 1 and shows that default as the field's placeholder (X ruling 431), in the suite and in the browser. The row that called the field's validator directly goes with it. Assisted-by: Claude Code:claude-opus-5-5
The order-count field's validator duplicated what the framework's number rules now do for any number field without one: hold it to the min and step in its custom attributes and store the canonical digits. It also rejected an emptied field, which the framework stores as cleared. The validator goes, so the field's min of 1 and step of 1 decide on the WooCommerce tab, and the field shows its default as its placeholder, so an emptied count, which reads as that default, shows it (X ruling 431). Assisted-by: Claude Code:claude-opus-5-5
Settings held every field array of the tab, so a plugin with many features would grow one class by every feature's fields and validators, and split it only once it had a second section, a point at which its code would restructure. The greeting's title, toggle, message and order count move to GreetingSettings in the greeting's folder, a final class whose public static get_settings_fields() returns them; Settings keeps the tab's registration, the plugin-level consent to delete data, and the assembly, which spreads the greeting's fields first. The tab renders and saves as before. Assisted-by: Claude Code:claude-opus-5-5
AGENTS.md, the architecture document, the scaffold README and the tests README still described typed defaults that the first step converted, a settings tab holding the greeting's fields and validator, and an end-to- end test that rejects an emptied count. They now say that each setting is one autoloaded option whose stored- form default the first migration seeds and its field reads, that a feature's fields live in its fields class, which Settings spreads into the tab, that removing the greeting replaces the seeding fingerprint, and what the suites now cover. Assisted-by: Claude Code:claude-opus-5-5
The settings-page and without-WooCommerce recipes still stored a page's fields in one array option, with a second family of keys, defaults and readers and a consent key the framework no longer accepts; the recurring-jobs recipe kept its repair gate among the settings, which the seeding loop would now store and fingerprint; and no recipe said how to add, rename or place a stored value, how other code changes a decision, or how a released @api symbol retires. The recipes now cover: - a setting: its key and stored-form default on Options, its field in its feature's fields class, which Settings spreads beside its own plugin-level fields, its reader, and the seeding step and fingerprint its release appends (X rulings 418, 419 and 425); - where stored data lives: settings, per-object overrides, list settings, entities, runtime state through OptionRows, and transients for markers that only spare repeated work (X rulings 422, 426, 433); - renaming a stored setting in a major release: a plain step appended after the steps earlier releases shipped and before the release's own batched and seeding steps, the old row winning, and the names a restricting setting keeps (X ruling 424); - the settings page on one option per field, with tabs and fields given apart, no validator and the consent kept under its own option in the without-WooCommerce steps (X rulings 416 and 420); - stored-form defaults on the Payments-tab fields; - the recurring jobs' repair gate as an hour-long transient named on the class that ensures the jobs (X ruling 433); - a filter at a decision point, carrying the object it decides for, instead of a filter on every settings read (X rulings 421 and 422); - retiring an @api function or constant a release shipped, and deleting one no release has (X ruling 432). Assisted-by: Claude Code:claude-opus-5-5
A step that names a setting through its Options constant keeps its code but not its behavior: once a later major renames the setting, the shipped step acts on the new name, so a site updating from before that step runs it against the new, still-empty row, and the rename's copy then brings back the value the step never converted. AGENTS.md's migrations convention now has every step name each stored name it touches, such as an option or meta key or a scheduled hook, and each default as a literal and read the stored rows directly, never through Options or another class's constant, with the seeding step, which follows Options::DEFAULTS by design, as the one exception (X ruling 434). The options convention excepts the migration steps from reading options only through Options, the architecture document says the components read them that way, and the rename recipe's example step names both options as literals. Assisted-by: Claude Code:claude-opus-5-5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Settings in the template now take the shape the framework's settings branch gave them, from its own tab to its recipes (X rulings 416–434).
Options::DEFAULTSholds every setting's default in the form its option stores, the greeting's message as an empty string. The first migration stores each absent default autoloaded in one loop, which a release that adds settings lists again, and each field's default reads its entry. A Unit row records a fingerprint of the key set each seeding step stores, and an Integration row holds each field's default to its entry (X ruling 418).src/Greeting/GreetingSettings.php;Settingskeeps the tab's registration, the consent to delete data and the assembly (X ruling 419).minandstepon WooCommerce's tab, and an emptied count stores empty and reads as the default 1, which the field shows as its placeholder (X rulings 420 and 431).@apifunction or constant a release shipped. Rewritten: the settings page and the without-WooCommerce steps on one option per field, the Payments-tab defaults in stored form, and the recurring jobs' repair gate as an hour-long transient (X rulings 416, 421–426, 432 and 433)."Update packages" moves the framework lock to its
8db8d53split, which needs no change in the template.Testing: Integration 91 tests on WordPress 7.1.4 with WooCommerce 11.3.0, the multisite group, the end-to-end suite, Unit, PHPCS, PHPStan, the PHP 7.4 parse, the scaffold rehearsal at the 28-character prefix and at
dws_ao, and both audits.