9B: Notices queue (merge after #8 — independent of #9A) - #10
Merged
Conversation
nikolaystrikhar
force-pushed
the
09-loader-resolve
branch
from
August 6, 2026 14:22
084004a to
bdb09e3
Compare
nikolaystrikhar
force-pushed
the
10-notices-queue
branch
from
August 6, 2026 14:22
2f976f3 to
a8e8860
Compare
Three defects, each of which loses the one warning a site owner gets that their plugin was deactivated. Storage was a transient. Verified in core: set_transient() short-circuits to wp_cache_set() and never touches the database when an external object cache is present, so on a Redis or Memcached site the queue lived only in the cache -- where wp_cache_flush(), which deploy scripts and every purge button call, destroys it. The merge notice is raised once and never re-queued. It is an option now. The queue was per-site while the deactivation is network-wide. The resolver passes $network_wide to deactivate_plugins(), removing the plugin from every site, but the explanation landed in whichever site's options table served the request. On a fifty-site network the superadmin would never find it. Multisite uses network options now. render() consumes the queue and had no capability check, and it is wired to a hook that fires for anyone who can reach wp-admin. A subscriber loading profile.php silently swallowed the notice, and nothing re-queues it. It checks activate_plugins first, which on multisite correctly resolves to superadmins. Also drop non-string entries instead of printing them, expose option_name() so a host can render the same queue without replacing the implementation, and cover the corrupted-queue, capability, cache-flush and missing-prefix paths.
…ference The queue's accessor moved out of this change, so the test suite no longer imports Loader, resets it, or asserts that it resolves the default notices. Nothing here depended on that reset for isolation: the option is deleted in setUp() and tearDown(), and the hook prefix is reset through Config_State, so each test starts from an empty queue and an unset prefix on its own. The two raw queries that read the option row straight out of the database now pass the table through the %i identifier placeholder instead of interpolating it into the SQL. The query text is a literal again, which is what wpdb::prepare() asks for and what static analysis was rejecting. Verified against WordPress core before collapsing the multisite branching: update_site_option() delegates to update_network_option( null, ... ), which outside multisite ends in update_option( $option, $value, false ) — or add_option( $option, $value, '', false ) on the first write. Autoload is off on both paths, which is what the queue needs. get_site_option() and delete_site_option() fall through to get_option() and delete_option() the same way, so one call is correct on either install type. Also gives the queue a README section of its own: the option name, that it is a network option on multisite, that rendering is gated on activate_plugins and consumes the queue, and that option_name() is public so a host can render the queue without replacing anything.
nikolaystrikhar
force-pushed
the
09-loader-resolve
branch
from
August 11, 2026 10:47
bdb09e3 to
73a4106
Compare
nikolaystrikhar
force-pushed
the
10-notices-queue
branch
from
August 11, 2026 10:47
a8e8860 to
5bd6fb0
Compare
nikolaystrikhar
changed the base branch from
09-loader-resolve
to
08-registrar
August 11, 2026 10:47
One class decided what a notice said, where it was kept, who could consume it and how it was drawn. Task 14 adds a fifth job to the same class by hanging the activation-error rewrite off it. Notice_Store owns the option, Notice_Renderer owns the markup and the severity map, and Notices keeps the interface, the message defaults and the capability gate. The gate stays with the orchestration because it guards clearing the queue as much as drawing it. Both collaborators are constructor arguments that default to the standard implementations, so new Notices() — what Loader::resolve() builds when the container holds no binding — behaves as before and Notices_Interface is untouched. NoticesTest is unchanged apart from three added tests covering the new seam.
Notices was a plural bag noun naming the subject rather than the job, and it read worst of the three once the split landed beside Notice_Store and Notice_Renderer, which do say what they do. The folder now carries the subject and the class carries the job, following Conflict\Resolver — which is also why Queue_Interface sits beside its implementation rather than in Contracts\. Contracts\Notices_Interface -> Notices\Queue_Interface Notices -> Notices\Queue Notice_Store -> Notices\Store Notice_Renderer -> Notices\Renderer No behaviour change: the interface keeps the same method set, and new Queue() does what new Notices() did. Renaming is free now and a breaking change after 1.0. The spec's collaborator table and the plan's deviation 7 record the move. The plan's Step 4/5 listings still show the pre-rename shape, as they still show the transient deviation 1 replaced. Branches 09-loader-resolve, 11-loader-load-path and 12-conflict-resolver still name the old type and will need the new import when they rebase.
nikolaystrikhar
requested review from
d4mation
and removed request for
d4mation
August 11, 2026 11:49
README keeps the docs-index structure from 08-registrar; this branch's notices documentation moves to docs/notices.md rather than growing the README back. In the plan, the Task 11 teardown takes one line from each side: all_admin_notices is this branch's deliberate hook change, and Loader_State::reset() is 08-registrar's fix for the no-test-seams rule. Tasks 1-6 shipped in PRs #1-#6, so their plan sections are removed and the rule for doing so is recorded in CLAUDE.md. CLAUDE.md's collaborator table, boot lifecycle and Keys section are corrected to match the split Notices\ classes and the option-backed queue.
d4mation
reviewed
Aug 11, 2026
- Introduced `Notices\Contracts\Queue_Interface` to define the structure for managing admin notices. - Updated `Config` class to include methods for generating option names with normalized prefixes, ensuring consistent storage keys. - Enhanced `Store` class to utilize the new option name generation method. - Updated documentation to clarify the distinction between hook names and option names, including examples of their usage. - Added unit tests to validate the new functionality and ensure proper handling of hook prefixes in option names.
- Updated `Notices\Queue` and `Notices\Contracts\Queue_Interface` to allow messages with markup, utilizing `wp_kses_post()` for safe rendering. - Modified `Notices\Renderer` to reflect changes in message processing, ensuring links and formatting are preserved while disallowing scripts and event handlers. - Revised documentation to clarify the handling of notices and the implications of markup in messages. - Enhanced unit tests to validate the new rendering behavior and ensure proper handling of markup in notices.
- Renamed the newly created unit test file from `NoticesActivationErrorTest.php` to `QueueActivationErrorTest.php` for better clarity and organization. - Made minor adjustments to the documentation to improve readability and consistency, including formatting changes in the `Notices\Contracts\Queue_Interface` section. - Ensured that the new test file aligns with the updated structure of the Notices classes.
d4mation
reviewed
Aug 11, 2026
# Conflicts: # CLAUDE.md
wp_kses_post() allows <p>, so a host message that already carries one was being nested inside another -- markup the parser resolves by closing the outer tag early and stranding a </p>, drawing an empty line above the notice. A <ul>, which the same allowlist preserves, cannot sit in a <p> at all. wpautop() wraps only what needs wrapping and turns a blank line into a real paragraph break.
d4mation
approved these changes
Aug 11, 2026
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.
What: the notice queue — merge, conflict and dependency messages — as
Notices\Queue,Notices\StoreandNotices\RendererbehindNotices\Queue_Interface.Usage: