Skip to content

finding: dispatcher services.search reports handlerReady: true for a slot with no mounted handler — the one slot #4318 did not reach #7939

Description

@hotlong

What this is

A finding, latent for the same reason #7602 was: nothing registers the search slot today, so the wrong branch is unreachable on every host that exists. Surfaced while implementing #7602 (PR #7937), which fixed the sibling field on the same slot and left this one deliberately untouched — it is a separate decision on a separate field, and folding it into that PR would have widened a scoped card.

Observed

packages/runtime/src/http-dispatcher.ts, in getDiscoveryInfo()'s per-service status map:

search: searchRegistered ? svcAvailable(undefined,undefined,searchSvc) : svcUnavailable('search'),

svcAvailable() returns handlerReady: true for any occupant that carries no __serviceInfo self-description. The map's own contract, stated a few lines above it, is:

handlerReady: true means the dispatcher has a real, bound handler for this route.

The dispatcher has no handler for search — no search entry in route-ledger.ts, no branch in http-dispatcher.ts, and the call site passes route: undefined precisely because there is nothing to advertise. So a filled slot yields a record that says, in one breath, "no route" and "the handler is ready".

That is the contradiction #4318 named and closed for cache / queue / job. Its remedy is already in this file: svcInProcess(name, svc), which returns handlerReady: false plus inProcessServiceMessage(name) — "this is a kernel-internal contract with no HTTP surface", said honestly. search is the one slot of that shape that was not converted, and the only reason the omission never showed is that no ISearchService has ever been registered (CORE_SERVICE_PROVIDER records 'search': null).

Why this is a finding and not a bug

Identical grading to #7602: declared and served agree today, by coincidence of an empty slot. The wrong record can only be produced by a host that registers a search service, and none exists in either repository.

Why it was left out of PR #7937

What a fix would look like

Almost certainly one line, mirroring #4318:

search: searchRegistered ? svcInProcess('search',searchSvc) : svcUnavailable('search'),

Worth deciding rather than assuming, on one point: inProcessServiceMessage('search')'s wording was written for kernel-internal contracts (cache/queue/job). A search engine is not kernel-internal in the same sense — it is an external engine with no dispatcher surface — so the message may want its own phrasing even if the handlerReady: false half is settled.

A fix should also carry a test: the same shape PR #7937 used for the capability — register a search service, assert handlerReady === false — since like every other assertion on this slot, it is vacuous unless the fixture fills it.

Filed unassigned and without a domain:* label so routing stays the triage seat's call (packages/runtimedomain:cli per the domain table, if that is the disposition).

Related: #4318 (the same contradiction, fixed for cache/queue/job), #7602 / PR #7937 (the capability half of this slot), #2462 / ADR-0076 D12 (honest self-report).

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions