Authoritative spec:
docs/design/squad-audit-v1.adoc(decided 2026-06-11). Where this document and the design doc disagree, the design doc wins. This document describes what is actually in the tree and the target it is moving toward.
The repo currently contains two architectures side by side. The apps are wired to the legacy one; the new engine is a library not yet consumed by any app. That gap is real and deliberate — do not document it away.
A swipe + listings flow over VerisimDB:
- API (
apps/api, Deno TypeScript — the approved 4-file carve-out) provides routes for profiles, swipes, listings, and matches. - Web (
apps/web) and Mobile (apps/mobile, Expo) consume the same API contract over HTTP. Neither app imports the matching engine directly. - Shared legacy modules (
packages/shared/src/domain.js,encoding.js,constants.js) own validation, the v1 compatibility scoring, and vectorization. - The apps have no tests.
Persistence model: entities are stored as VerisimDB hexads using the
document + optional vector modalities.
Entity kinds: flatmate_profile, flatmate_listing, flatmate_swipe.
Payload encoding strategy (intentionally pragmatic for current VerisimDB API constraints):
- Add searchable tokens (
flatmate_profile,borough_camden, etc.) to the hexad title. - Append a base64url payload to the title for deterministic decode on read.
- Keep the body as human-readable JSON for audit/debug.
Legacy matching flow: create profile → deterministic 384-dim vector → write to
VerisimDB → feed endpoint ranks via vector search + compatibility re-score →
likes/dislikes stored as flatmate_swipe → mutual likes become matches.
Legacy listing flow: post listing with borough/rent → stored as
flatmate_listing → queried via text search + API-side filters.
packages/shared/src/matching/ is a pure-JS, side-effect-free library
(10 modules + 5 test files, 35 tests passing). It implements the design doc's
engine spec:
- Behaviour vs tolerance as separate, directional variables per dimension;
the weakest direction governs
(
min(fit(A.behaviour → B.tolerance), fit(B.behaviour → A.tolerance))). - Feasibility and harmony as split sub-scores, never blended; budget pairs score by overlap coefficient.
- Banded verdicts (
strong / workable / risky / not_viable) instead of raw integers; hard dealbreakers test the other's behaviour, never their tolerance. auditGroup(group.js): full pairwise matrix, budget by intersection (never mean), dispersion detection (split groups reported as split, not averaged), shared-space forecast = minimum member cleanliness with friction nodes named, weakest pair named.- Conceal lattice (
conceal.js): disclosure tiers T0 (band) / T1 (relational) / T2 (full), witheffectiveTier(a, b) = min(a.tier, b.tier)reciprocity. Mirrors the formally verified Idris2 disclosure core inproven-servers(protocols/proven-epistemic; ADR:proven-servers/docs/decisions/0002-add-proven-epistemic-disclosure-core.md). - Artifact generators:
agreement.js(house agreement draft from the group's actual divergence points, plus universal access-&-needs and guarantor sections — zero stored signal) andbrief.js(search brief with budget intersection, move-in window, HMO licensing note). - Deterministic seeds (
seed.js), including the deliberately broken "Bloomsbury Three" fixture (contains internal dealbreaker kills) as a labelled regression case.
Engine version: ENGINE_VERSION = "2.0.0" (matching/constants.js).
The v1 product is the Squad Audit: an already-formed group gets a harmony + feasibility audit, an auto-drafted house agreement, and a search brief — upstream of SpareRoom, no listings. Target wave: Jan–May 2027 London second-years.
The server target is an epistemic-typed server on VerisimDB:
- Every stored field registered with governance metadata
{purpose: identity|feasibility|harmony|agreement, revealingness: low|moderate|sensitive, minTier: 0|1|2}. - That registry is the single source for read-time tier gating, the conceal lattice's redaction rules, and the future retention schedule / DPIA documentation (Art. 25 data-protection-by-design — a risk reducer, not a compliance exemption).
- No UK GDPR Art. 9 (special-category) fields by schema construction —
e.g. no
accessibilityNeedsfree text; universal unconditional agreement/brief sections replace stored sensitive signals. - Product copy and mechanism design never promise a "stable matching" — nonexistence is the typical case for roommate markets at scale (Chin & Michelen 2026); see the theory-constraints section of the design doc.
Wiring path (none of this exists yet): apps move from the swipe/listings contract to group-audit endpoints backed by engine v2 plus the field-governance registry; legacy v0 stays in-tree as legacy until a quarantine/removal decision.
- Single-city launch (London) is hardcoded by design in the legacy
constants (
packages/shared/src/constants.js); the audit flow itself is location-light, with borough prefiltering deferred until discovery lands. - Auth is out of scope for the first instance;
userIdis client-supplied. - Matching rules live in
packages/shared/src/matching/and never in UI components; the conceal/redaction layer lives in the engine module for the same reason. - VerisimDB availability determines persistence (sole persistence layer — estate invariant).