Skip to content

gemma-faceted-search: empty facet dimensions are not disabled, and the facet cache is never invalidated on data change #483

Description

@rubenvdlinde

Summary

Two scenarios in openspec/specs/gemma-faceted-search/spec.md describe
behaviour that is not implemented. Neither has been given an
@e2e exclude — an exclusion claims "a browser cannot observe this", and both
of these are perfectly observable; they simply do not happen. Excluding them
would buy a green gate-19 with a false statement, so they are being left in the
count and filed here instead.

1. A facet dimension with zero available values is NOT visibly disabled

Spec (Requirement: Facet sidebar UI on the module and dienst index pages):

THEN the applicatieservice facet section MUST indicate it has no available
values (e.g. empty state or disabled state)
AND MUST NOT be selectable

CnFacetSidebar.vue binds no :disabled on its NcSelect, and
getFilterOptions() returns [] for a dimension with no values while the
control still renders enabled. There is no empty-state branch in
src/views/FacetedCatalogIndexView.vue, src/utils/facetSchema.js, or
src/store/modules/facets.js.

Confirmed reachable on a live instance: GET /api/facets/module on a fresh
register returns

{"referentiecomponent":[],"standaard":[],"applicatieservice":[],"domein":[],"_meta":{...}}

— all four dimensions empty, and all four render as normal, selectable
pickers.

Note this control lives in @conduction/nextcloud-vue, so the fix is probably
a :disabled/empty-state prop on CnFacetSidebar there rather than in this
app.

2. The facet cache is never invalidated by a data change

Spec (Requirement: Facet aggregation results are cached):

WHEN a module's referentieComponenten, standaarden, standaardenGemma,
or related GEMMA link field is created, updated, or removed
THEN the cache for affected facet queries MUST be invalidated
AND the next facet request MUST recompute the aggregation

The cache is real (FacetService, ICacheFactory::createDistributed(prefix: 'softwarecatalog_facets'), CACHE_TTL = 1800) but expiry is TTL only.
There is no invalidation path: no registerEventListener in
lib/AppInfo/Application.php touches facets, lib/EventListener/ contains no
facet listener, and no code outside FacetService references the cache prefix.

So for up to 30 minutes after a GEMMA link changes, the facet panel serves
stale counts. This is a correctness bug with a user-visible symptom ("I edited
the module and the filter still shows the old number"), not just a spec gap.

The sibling scenario "Repeated identical facet request is served from cache"
IS satisfied and IS observable (_meta.cached is echoed in the response body).

Why these are filed rather than excluded

Both are browser-observable in principle — the first is pure DOM state, the
second is a two-request sequence around a data edit. The only reason a test
cannot pass today is that the behaviour is absent. That is a defect, not a
limit of the observation method.

Related

Found while closing gate-19 (e2e-coverage) for softwarecatalog. The five
scenarios in this spec that genuinely cannot be observed from a browser
(query-array construction, paging-vs-scanning, code-path identity, cache-key
composition, l10n key names) have been given reason-bearing @e2e exclude
markers in the same PR; these two deliberately have not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions