search() does the right thing today: one build_filter(tenant_id, ...), applied to all three client.query_points branches. The isolation this repo exists to demonstrate is real.
It is also entirely conventional. Nothing stops a new code path from calling client.query_points with its own filter, or none, and the failure is silent: the query succeeds, returns other tenants' documents, and no test notices unless it happens to look.
For a reference implementation this matters more than usual. People will copy the shape of this code into their own systems, and the shape should carry the guarantee.
Proposal
Pick one and document why:
- A thin client wrapper that is the only thing allowed to call
query_points, and which takes tenant_id as a required argument and builds the filter itself. A raw filter cannot be passed.
- A CI check that fails if
query_points, scroll or retrieve is called anywhere outside that wrapper.
Option 1 makes it structural; option 2 keeps the code flat and catches it at review time. Both would be better than the current position.
Acceptance
- A deliberately leaky query path added in a test fails CI
search() behaviour is unchanged
- The README explains the mechanism, since that is the part worth copying
search()does the right thing today: onebuild_filter(tenant_id, ...), applied to all threeclient.query_pointsbranches. The isolation this repo exists to demonstrate is real.It is also entirely conventional. Nothing stops a new code path from calling
client.query_pointswith its own filter, or none, and the failure is silent: the query succeeds, returns other tenants' documents, and no test notices unless it happens to look.For a reference implementation this matters more than usual. People will copy the shape of this code into their own systems, and the shape should carry the guarantee.
Proposal
Pick one and document why:
query_points, and which takestenant_idas a required argument and builds the filter itself. A raw filter cannot be passed.query_points,scrollorretrieveis called anywhere outside that wrapper.Option 1 makes it structural; option 2 keeps the code flat and catches it at review time. Both would be better than the current position.
Acceptance
search()behaviour is unchanged