~bigbes/sr-ht-spec: e0938e94

refactor: make the filter-polarity trap inexpressible

service.SpaceFilter meant "empty membership selects nothing" — a newly
created project has no members — while search.Query.Spaces was a bare
[]core.SpaceRef whose empty case meant every space. Passing a project's
members into a query therefore turned an empty project into the whole
corpus: a silent scope inversion, invisible when it happens, and passing
every test written with a non-empty project.

The filter moves to core.SpaceFilter with unexported fields, and
search.Query takes it whole. There is no slice to hand over any more, so
the inversion cannot be written. Its three states are distinct: every
space (EverythingFilter), exactly these — possibly none — (SpacesFilter),
and the zero value, which is neither. Search refuses the zero value
rather than defaulting it, because both plausible defaults are wrong for
one of the two callers that can produce one, and returns no hits for a
filter that selects no space without asking the index.

service.SpaceFilter is now an alias for the core type, so ResolveProject
and its callers keep their names. Tests that built a Query without a
scope now say core.EverythingFilter(), which is what they always meant.

Eugene Blikh <bigbes@gmail.com> — 2026-07-22 15:03:19 UTC

Commit e0938e942893dda1670ac125b4785c8b8941bfe7view raw patch

Parent(s): 5a10600a

12 changed file(s)

FileStatus+
core/errors.go M +4 -2
core/spacefilter.go A +160
core/spacefilter_test.go A +94
mcpsrv/mcpsrv_test.go M +7 -3
mcpsrv/search.go M +11 -6
search/index_test.go M +70 -32
search/mixed_test.go M +9 -7
search/search.go M +27 -6
service/project.go M +17 -69
service/project_test.go M +23 -12
web/handlers.go M +5 -2
web/web_test.go M +5 -2