Summary
Conditional visibility is never enforced. Rules can be created, listed, edited and previewed, and none of it changes what any user sees. ConditionalService::checkRulesForPlacement() is called from exactly one place in production — the endpoint that reports a placement's rules — and from no dashboard or placement read path.
This makes REQ-CVUI-005's central guarantee false today:
the preview visible verdict MUST equal the render-time visibility verdict
AND this equality MUST hold because both paths execute the same evaluation code
There is no second path.
Three independent observations, in the order they were taken
1. Browser. Save an include rule with window 2020-01-01 .. 2020-12-31 through the Visibility rules editor, reload the dashboard: the placement still renders. The editor previews the very same rule set as Hidden a moment earlier, in the same modal.
2. HTTP, status code printed. Seed the same rule through the API and read the dashboard back:
POST /index.php/apps/launchpad/api/widgets/239/rules
{"ruleType":"date","ruleConfig":{"startDate":"2020-01-01","endDate":"2020-12-31"},"isInclude":true}
-> 201
GET /index.php/apps/launchpad/api/dashboard/43
-> 200
placements: id=235 isVisible=1 | id=239 isVisible=1 (VISPROBE) | id=236 … | id=237 … | id=238 …
Placement 239 carries an include rule that cannot match, and comes back with isVisible: 1.
3. Callers.
$ grep -rn "checkRulesForPlacement" lib/
lib/Controller/RuleApiController.php:98: $isVisible = $this->conditionalService->checkRulesForPlacement(
lib/Service/ConditionalService.php:93: public function checkRulesForPlacement(int $placementId, string $userId): bool
lib/Service/VisibilityChecker.php:47: * callers (`ConditionalService::checkRulesForPlacement()`) keep calling
lib/Service/RuleEvaluatorService.php:58: * (`ConditionalService::checkRulesForPlacement()`) never pass them, so
The single production caller is RuleApiController::getRules() — the editor's own read, which is why GET /api/widgets/{id}/rules correctly answers {"rules":[…],"isVisible":…}. No dashboard controller, no placement serialiser, no initial-state provider calls it.
grep -rn "isWidgetVisible" lib/ returns nothing, so the render-time entry point REQ-CVUI-005 names by that name does not exist either.
Observation 3 is an absence claim, so it was taken with a positive control: the same grep for previewRules finds its caller (VisibilityPreviewController.php:123) immediately. The search works; there is nothing to find.
Note that two docblocks assert the opposite — VisibilityChecker.php:47 and RuleEvaluatorService.php:56-58 both describe "render-time callers (ConditionalService::checkRulesForPlacement())". They describe a caller that was never written. That is very likely how this survived review.
Severity
Conditional visibility is presented to authors as a working feature: the editor loads, saves, and gives a confident Visible / Hidden verdict with a matched-rule explanation. An author who scopes a widget to a group, or to a date window, will reasonably believe the widget is hidden from everyone else. It is not — it renders for every user with access to the dashboard.
If any rule anywhere was intended to keep content away from an audience, that expectation is not being met. Worth checking whether any live dashboard relies on it before this is prioritised.
Relationship to the other two
All three were found writing gate-19 round-3 coverage for conditional-visibility-editor. Together they mean the capability is, end to end, an editor over data nothing reads.
Impact on gate-19
Two scenarios cannot be covered and are left uncovered and NOT @e2e excluded (an exclusion claims a browser cannot observe the scenario; a browser is exactly what observed it failing, and per .github#345 the gate would score the exclusion as positive coverage):
REQ-CVUI-001 editor-does-not-change-evaluation-semantics
REQ-CVUI-005 preview-verdict-matches-render-time-verdict-for-identical-inputs
Both are recorded in tests/e2e/conditional-visibility-editor.spec.ts where the tests would otherwise sit, including the evidence above, so the next person does not have to rediscover it.
Summary
Conditional visibility is never enforced. Rules can be created, listed, edited and previewed, and none of it changes what any user sees.
ConditionalService::checkRulesForPlacement()is called from exactly one place in production — the endpoint that reports a placement's rules — and from no dashboard or placement read path.This makes REQ-CVUI-005's central guarantee false today:
There is no second path.
Three independent observations, in the order they were taken
1. Browser. Save an include rule with window
2020-01-01 .. 2020-12-31through the Visibility rules editor, reload the dashboard: the placement still renders. The editor previews the very same rule set as Hidden a moment earlier, in the same modal.2. HTTP, status code printed. Seed the same rule through the API and read the dashboard back:
Placement 239 carries an include rule that cannot match, and comes back with
isVisible: 1.3. Callers.
The single production caller is
RuleApiController::getRules()— the editor's own read, which is whyGET /api/widgets/{id}/rulescorrectly answers{"rules":[…],"isVisible":…}. No dashboard controller, no placement serialiser, no initial-state provider calls it.grep -rn "isWidgetVisible" lib/returns nothing, so the render-time entry point REQ-CVUI-005 names by that name does not exist either.Observation 3 is an absence claim, so it was taken with a positive control: the same grep for
previewRulesfinds its caller (VisibilityPreviewController.php:123) immediately. The search works; there is nothing to find.Note that two docblocks assert the opposite —
VisibilityChecker.php:47andRuleEvaluatorService.php:56-58both describe "render-time callers (ConditionalService::checkRulesForPlacement())". They describe a caller that was never written. That is very likely how this survived review.Severity
Conditional visibility is presented to authors as a working feature: the editor loads, saves, and gives a confident Visible / Hidden verdict with a matched-rule explanation. An author who scopes a widget to a group, or to a date window, will reasonably believe the widget is hidden from everyone else. It is not — it renders for every user with access to the dashboard.
If any rule anywhere was intended to keep content away from an audience, that expectation is not being met. Worth checking whether any live dashboard relies on it before this is prioritised.
Relationship to the other two
All three were found writing gate-19 round-3 coverage for
conditional-visibility-editor. Together they mean the capability is, end to end, an editor over data nothing reads.Impact on gate-19
Two scenarios cannot be covered and are left uncovered and NOT
@e2e excluded (an exclusion claims a browser cannot observe the scenario; a browser is exactly what observed it failing, and per.github#345the gate would score the exclusion as positive coverage):REQ-CVUI-001 editor-does-not-change-evaluation-semanticsREQ-CVUI-005 preview-verdict-matches-render-time-verdict-for-identical-inputsBoth are recorded in
tests/e2e/conditional-visibility-editor.spec.tswhere the tests would otherwise sit, including the evidence above, so the next person does not have to rediscover it.