A university deciding whether to run a pilot asks one question before it asks about features, and it is always a version of the same one: our professors' cases, our students' progress — who else on this platform can reach them? A demo cannot answer that. The answer lives in the code that decides, and in whether anything checks that the code keeps deciding the same way. Over the past few days that machinery in VARGATES Medical was rebuilt, and this is what it turned out to involve.

One fact with two places to read it

Until this week two records said where a person belongs. A column on the account held one institution; a membership table held a set of them, each row with a role. The decisions that grant access read the column. Eight other readers read the table.

That is not a tidiness complaint. The column cannot represent states the product creates routinely: a student who belongs to one institution and is enrolled by code to a professor from another, an administrator of two campuses, someone who teaches at one institution and studies at another. One column holds one value, so each of those reads wrong in one direction or the other — refusing a person their own campus, or admitting them to somebody else's.

The census went over the codebase rather than over the ticket, and found the column being read in modules the ticket never named. Whole surfaces sat on it — which tenant's dashboard an institution administrator sees, which institution's learning packages are listed, the institution stamped on a newly authored case. All of them ask membership now, and the authentication dependency no longer hands the column out at all: asking for it raises, rather than quietly deciding on a value that may be stale.

A filter that was valid SQL and admitted everybody

The first version of the visibility clause is worth printing, because nothing about it looks wrong.

-- the clause is built by a helper, from a column name and a parameter
belongs_to("institution_uuid", "$1")

-- expanded inside a correlated EXISTS over institution_memberships m,
-- where the bare column binds to m, so the clause read
m.institution_uuid = m.institution_uuid
True for every membership row, so an account belonging to any institution read every institution's cases.

Valid SQL. No error, no warning, and a page that looks entirely plausible — and it fails open, which is the direction that does not announce itself. Reading the diff would not have found it; an end-to-end test did. The helper that builds the clause now refuses to build one at all: a membership column that would bind inside the subquery is rejected on either side of the comparison, and the message names the accepted form.

The window next to the door

With the door fixed, the boundary still had a window. The recommendations endpoint was the one case-listing surface that never went through the shared filter: it hand-wrote its condition three times, and all three said status and nothing about visibility. The sharp end was the fallback branch — any published case to any authenticated caller, with no other constraint at all.

What that surfaced was the catalogue block of a case its author had marked private: title, description, specialty, difficulty, chief complaint, author name, and the patient's age and sex. The clinical body is withheld by a separate function and stayed withheld. Of the other two branches, one intersects the caller's own progress rows, so reaching it means having legitimately opened the case once. The second only picks a specialty from those rows — the cases it then returns need never have been opened at all.

All three branches call the shared filter now, so every listing surface reads one definition of which cases an account may see. What keeps it at one is a guard that counts provenance: for every function that queries the case table, how many of its queries carry the shared filter, against how many times that function asked the helper for one. A fourth hand-written query cannot join the pattern without declaring itself, with a reason, in the baseline.

A parameter accepted, validated and discarded

An account belonging to several institutions now says which one it is acting for. Both resolvers of that parameter took it and dropped it for one role: a platform administrator naming an institution got platform-wide numbers back. No exposure — an administrator may see everything — but the answer was indistinguishable from one that had honoured the request. An administrator comparing two institutions by swapping the identifier saw the same totals twice, with nothing to say whether the institutions match or the parameter did nothing. And that is the ordinary way to ask whether the seats an institution holds are actually being used.

Naming an institution you do not belong to is refused, and the refusal says nothing about whether that institution exists — answering “no such institution” would turn the parameter into a membership oracle for anyone with an identifier to try. An identifier that names nothing at all is an error rather than zeroes, because an empty dashboard reads as an institution with no activity.

The guards, including two that were decorative

The guard that watches these routes selected them by looking for the institution in a route's signature. A route that names it in the request body carries it inside a model instead, so the guard walked past — and stayed green while doing it. Selection is by feature now, signature or model, and the number of routes it holds is pinned: fewer means the selector stopped matching, and silence is not a pass.

The role arm is an allow-list rather than an exclusion, so a role added to the product later cannot silently acquire institution scoping. That was proved by mutation, and the first attempt at proving it failed: mutated back to an exclusion, all twelve tests stayed green, because the teacher in the fixture had no memberships and a later check refused them anyway. The test was decorative. Giving that teacher a real membership is what turned it into a measurement — re-mutated, four failures.

The provenance guard took three attempts. Asking whether any query in a function uses the shared filter lets a compliant sibling vouch for a bypassing one. Asking whether each query interpolates the filter is blind to a condition assembled by hand, because the query text does not change. Counting filter-bearing queries against calls to the helper is what shipped — and the blind spot that remains is written into the test rather than left to be found.

The backfill decides by history, not by generosity

Moving the decision to memberships left some accounts with a column pointing at an institution and no membership row to match it. The migration restores one only where there are traces the person actually was there: an enrolment in that institution's context, a grant it issued, progress on an assignment it set, teaching done there — or, for an administrator, who never studies and would otherwise read as a stale column, an action recorded against it: a grant issued, an invitation sent, a group, a track or a package created. Every trace is scoped to the institution in question, because an unscoped version can only ever return a larger set — the direction that looks like diligence while handing out access earned somewhere else.

It inserts and deletes nothing, and the reverse migration is deliberately empty: nothing distinguishes the rows it wrote from rows written since by an ordinary invitation, so a reverse would revoke real people's access. Rolling the code back is the recovery path.

The cost is one indexed lookup per request, where the value used to be free in hand. It is deliberately not cached in the session token: a token outlives a membership change, and a stale claim inside a token is harder to correct than a stale column in a database.

If you are the one evaluating a platform

None of this was found by a customer. It was found by our own tests, and by the census that preceded them. The same run closed the gap underneath: creating an institution requires a platform administrator, and a count of production accounts on 28 August returned none of that role. One had been inserted straight into the database to unblock the work, which is not a path anybody should have to repeat — so this run built the repeatable one, a script that creates, promotes or resets that account, and a second whose only job is to answer whether an environment has one at all.

So the useful question in an evaluation is not whether a platform is multi-tenant. The answer to that is always yes. Better ones: where does it read “which institution does this account belong to” from, and how many places decide it; what happens to an account that belongs to two; and what fails, loudly, when a new listing endpoint forgets the boundary. Ask which test turns red. If the answer is that a reviewer would notice, then the boundary is held by attention — and attention is the thing that runs out.

VARGATES Medical is at medical.vargates.com.

Start a conversation