Found in the 1.0.0-alpha9 pre-release audit (finding I2). Info-level: the impact is transient and UI-only (a status message dropped); no engine state, tokens, work items, or persisted data are affected. Filing it because the retract mechanism is fragile and worth hardening.
Where
modules/orchestra_interaction/src/ContextMessages.php: display() posts the context to the messenger and records it in $posted; retract() withdraws it.
What happens
display() posts each resolved context message to the messenger and records its rendered string under $posted, keyed by severity. retract() then calls the messenger's deleteByType() for that severity, which drains every flash message of that severity, and re-adds only those whose string does not equal an entry in $posted. Message identity is therefore purely rendered-string plus severity.
Two consequences follow:
- A coincidentally identical message from any other source at the same severity is dropped and never re-added, which contradicts the retract() docblock claim that "other modules' messages are left untouched."
- ContextMessages is a request-shared singleton and $posted accumulates across every display() call, so two orchestra webforms rendered on one page share $posted: submitting one deletes all status messages of that severity and suppresses re-adding the other form's still-relevant context.
Why it is low impact
Both sub-scenarios are logically reproducible, but the effect is a transient flash message dropped, nothing more. The collision window is small: the message format is distinctive (a label and value line) and core de-dups identical adds, and pages with two orchestra interaction forms are unusual. ContextMessagesTest exercises retract() with a distinct unrelated string but does not cover the identical-string collision.
Fix direction
Withdraw only the messages this service posted, by tracking a per-message identity rather than the rendered string: keep the posted message objects (or a stable per-message marker) for the request and remove exactly those, instead of deleteByType() plus a string-compared re-add. Add tests covering the identical-string collision (an unrelated message with the same text and severity must survive retract()) and the two-forms-on-one-page case.
Issue fork orchestra-3608544
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
mably commentedOpened MR !293 against 1.x.
retract() is now scoped per step: display() and retract() key their record by (instance, token), so completing one step withdraws only its own postings and withdraws at most as many of each rendered string as it posted; another step's context (a second orchestra form on the page) and other modules' messages survive. A new ContextMessagesTest case covers this and was verified to fail without the scoping.
One limit is documented rather than fixed: Messenger::addMessage normalizes every message to a plain Markup and collapses equal strings, so a per-message object marker cannot survive the API and a byte-identical string from another source cannot be told apart. The per-step, count-bounded scoping is the strongest guarantee the public messenger API allows. Local run: ContextMessagesTest (7) and OrchestraInteractionHandlerTest (15) green, phpcs and cspell clean. Setting to Needs review pending CI.
Comment #5
mably commented