Skip to content

Commit eabfb24

Browse files
hyperpolymathclaude
andcommitted
docs(wiki): comprehensive wiki source (8 pages) pending wiki-repo initialization
GitHub only materializes <repo>.wiki.git after a first page is created in the web UI; until then these are versioned here, SPDX-headed via invisible HTML comments so they publish verbatim. Publishing instructions in docs/wiki/README.adoc. All claims execution-verified (audits run on seed fixtures; 35-test suite). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8c11cb2 commit eabfb24

9 files changed

Lines changed: 677 additions & 0 deletions

docs/wiki/Conceal-Lattice.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<!--
2+
SPDX-License-Identifier: MPL-2.0
3+
Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
-->
5+
# Conceal Lattice
6+
7+
The disclosure model for everything the engine says about a pair of people. A compatibility result is personal data about **two** data subjects at once; the conceal lattice governs who may learn what, with **reciprocity** (LinkedIn semantics: you may see at most what you yourself grant). Implementation: `packages/shared/src/matching/conceal.js`; spec §8 of [`docs/design/squad-audit-v1.adoc`](../blob/main/docs/design/squad-audit-v1.adoc); decision summary in [[Design-Decision-Record]].
8+
9+
## The tiers
10+
11+
`TIER = { BAND: 0, RELATIONAL: 1, FULL: 2 }` (in `constants.js`).
12+
13+
| Tier | Name | What A sees of B |
14+
|---|---|---|
15+
| **T0** | Band | The verdict band only (`strong` / `workable` / `risky` / `not_viable`). Dealbreakers fold into `not_viable` — no attribution, no dimension. Nothing else: `redactResult` at T0 returns `{ tier: 0, verdict }`. |
16+
| **T1** | Relational | T0 plus the `harmony`/`feasibility` scores, `dealbreakerHit`, and **relationally phrased** reasons/warnings ("guest norms clash", "budgets overlap comfortably") rebuilt from the structured `factors`. Dealbreakers name the *dimension* ("hard incompatibility between you: a smoker") but never who holds the rule or who triggers it. No habit values, no names. Unanswered dimensions are disclosed only as "some dimensions are unanswered and were scored neutrally". |
17+
| **T2** | Full | The unredacted result: full reasons, warnings, factor tables, and attributed dealbreakers (`{token, label, holder, trigger}`). The default **within a consented audit group** — joining an audit group elects T2 within that group. |
18+
19+
## Reciprocity: the meet
20+
21+
```js
22+
effectiveTier(tierA, tierB) === Math.min(tierA, tierB)
23+
```
24+
25+
What A sees of B is governed by the lattice **meet** of the two grants. Consequences:
26+
27+
- it is symmetric — neither side can arrange to see more than the other;
28+
- you never see above what *you* granted, and never above what *they* granted;
29+
- one party choosing T0 caps the whole exchange at band level (deny-by-default absorbs).
30+
31+
`discoverMatches(..., { tier })` applies `redactResult` at the discovery boundary, so redaction is a property of the engine's output surface, not a UI courtesy (see [[Engine-v2-Reference]]).
32+
33+
## No string surgery
34+
35+
Tier-1 views are **rebuilt** from the structured `factors` and `dealbreakers` records using the `NARRATION` table (whose phrasings are relational by construction), never by editing tier-2 strings — string surgery leaks. This is why `scoreCompatibility` emits structured dealbreaker records rather than only prose warnings. The test suite asserts the T1 view leaks no names, no attribution, and no habit values.
36+
37+
## The self-inference caveat
38+
39+
Redaction cannot prevent **self-inference**: a smoker told "hard incompatibility between you: a smoker" learns the counterpart holds an anti-smoking dealbreaker *from their own data*. This is inherent to explainable matching — any disclosed function output reveals information about its inputs — and the project's posture is that it is **disclosed, not denied**. (It is an epistemic fact about functions, not a gap in the lattice; differential privacy and SMPC address different layers and are explicitly out of scope of this core.)
40+
41+
## The formally verified core
42+
43+
`conceal.js` deliberately **mirrors** a formally verified reference semantics: `proven-servers/protocols/proven-epistemic` (Idris2), adopted by ADR [`proven-servers/docs/decisions/0002-add-proven-epistemic-disclosure-core.md`](https://github.com/hyperpolymath/proven-servers/blob/main/docs/decisions/0002-add-proven-epistemic-disclosure-core.md). There, the properties this page asserts in prose are **machine-checked theorems** over the tier lattice (`Band < Relational < Full`):
44+
45+
| Theorem | Statement | Meaning here |
46+
|---|---|---|
47+
| `meetSym` | `meet a b = meet b a` | Reciprocity is symmetric (the "LinkedIn property"). |
48+
| `meetLowerLeft` | `meet a b ≤ a` | You never see above what **you** granted. |
49+
| `meetLowerRight` | `meet a b ≤ b` | You never see above what **the other party** granted. |
50+
| `bandAbsorbs` | `meet Band t = Band` | Deny-by-default is absorbing: one refusing party caps the session at BAND. |
51+
52+
The JS in this repo is the consumer-side mirror, kept small enough to audit by eye against those theorems; the Idris2 core also carries the field-governance types (`{purpose, revealingness, minTier}` — see [[Data-Governance]]) and a session lifecycle in which over-tier disclosure is unrepresentable rather than merely checked. The "epistemic type server" framing is the estate's coinage, but the substance is classical lattice-based information-flow control (Denning 1976) in dependent types.
53+
54+
**Honest status note:** the lattice is enforced today inside the engine library (and exercised by tests). Server-side read-time enforcement is part of the epistemic-typed server target — the apps are not yet wired to engine v2, so no deployed surface currently exercises these tiers.

docs/wiki/Data-Governance.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<!--
2+
SPDX-License-Identifier: MPL-2.0
3+
Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
-->
5+
# Data Governance
6+
7+
flat-mate's data posture in one sentence: **ordinary UK GDPR Art. 6 controller duties accepted; no Art. 9 (special-category) data by schema construction; every stored field carries machine-readable governance metadata.** This is Art. 25 data-protection-by-design — a risk reducer and obligation organiser, **not** a compliance exemption. Spec §5/§5.1 of [`docs/design/squad-audit-v1.adoc`](../blob/main/docs/design/squad-audit-v1.adoc).
8+
9+
## No Art. 9 by construction
10+
11+
The schema (`packages/shared/src/matching/profile.js`) contains **no special-category fields, and no fields from which one is straightforwardly derived**:
12+
13+
- **Removed: `accessibilityNeeds` free text.** It is Art. 9 health data — and even a boolean flag indirectly reveals disability. The governing authority is CJEU C-184/20: derived data liable to reveal a sensitive attribute inherits special-category status. So neither the text nor the flag exists.
14+
- **Never stored: guarantor status.** Whether a student has a UK guarantor is a strong nationality proxy; storing or filtering on it creates indirect-discrimination exposure. No field exists.
15+
16+
The constants registry states this as policy: there are **deliberately no `revealingness: "sensitive"` fields** in the schema; any future sensitive field must set `minTier: 2` *and* go through explicit-consent design first (modelled as `WellGoverned` in the proven core — see [[Conceal-Lattice]]).
17+
18+
## The universal-prompt pattern
19+
20+
Removing the fields does not remove the *needs* — it relocates the conversation to where no data is stored. The pattern: **prompt the conversation unconditionally for every group, store zero signal.**
21+
22+
| Need | Where it surfaces | What is stored |
23+
|---|---|---|
24+
| Access needs | The house agreement draft (`agreement.js`) includes an **"Access & needs"** section for *every* group, unconditionally: "is there anything about the home setup you need to work — access, allergies, medical storage, prayer or study space, temperature, anything else? Discuss in person; the platform deliberately stores nothing here." | **Nothing.** Because the section is universal, its presence reveals nothing about any member. |
25+
| Guarantors | The agreement's **"Guarantors & tenancy setup"** section and the search brief checklist (`brief.js`) carry an unconditional per-member guarantor confirmation item ("the platform stores no guarantor data"). | **Nothing.** |
26+
27+
Same conversation prompted, zero stored signal. See [[Squad-Audit-Walkthrough]] for these sections in the real artifact output.
28+
29+
## The field governance registry
30+
31+
Every profile field is registered in `FIELD_GOVERNANCE` (`packages/shared/src/matching/constants.js`) with three coordinates:
32+
33+
```
34+
{ purpose: identity | feasibility | harmony | agreement,
35+
revealingness: innocuous | contextual (sensitive: deliberately unused),
36+
minTier: 0 | 1 | 2 }
37+
```
38+
39+
`minTier` gates the **raw value**; tier-1 views carry only relational narration rebuilt from structured factors (see [[Conceal-Lattice]]). Current registry, as in code:
40+
41+
| Fields | purpose | revealingness | minTier |
42+
|---|---|---|---|
43+
| `name`, `universityEmail` | identity | contextual | 2 (FULL) |
44+
| `university`, `moveInFlexDays`, `leaseLengthMonths` | feasibility | innocuous | 1 (RELATIONAL) |
45+
| `budgetMin`, `budgetMax`, `preferredMoveInDate` | feasibility | contextual | 1 (RELATIONAL) |
46+
| all harmony fields (`cleanliness`, `cleanlinessTolerance`, `bedtime`, `noiseGenerated`, `noiseTolerance`, `socialPreference`, `guestFrequency`, `guestTolerance`, `partnerStays`, `smoking`, `smokingTolerance`, `drinking`, `drinkingTolerance`, `hasPets`, `petTolerance`, `dealbreakers`) | harmony | contextual | 2 (FULL) |
47+
| `conflictStyle`, `billSplitPreference` | agreement | contextual | 2 (FULL) |
48+
49+
This registry is designed to be the **single source** for (a) read-time tier gating on the server, (b) the conceal lattice's redaction rules, (c) the future retention schedule and DPIA documentation. The schema mirrors the formally verified field-governance types in `proven-servers/protocols/proven-epistemic`.
50+
51+
## Adjacent honesty: what the engine refuses to infer
52+
53+
- Missing answers are stored as `null` and scored neutrally (0.5) **with a warning** — absence of evidence never scores better than adverse evidence ([[Engine-v2-Reference]]).
54+
- The disclosed-output caveat is owned, not hidden: redaction cannot prevent *self-inference* from one's own data ([[Conceal-Lattice]]).
55+
56+
## What remains owed before launch
57+
58+
Stated in the spec and restated here so it cannot quietly disappear:
59+
60+
1. **Privacy notice** (Art. 13/14).
61+
2. **Retention schedule**, driven off the `FIELD_GOVERNANCE` registry.
62+
3. **Verified VerisimDB erasure + subject-access-export story** — VerisimDB is the estate's sole persistence layer, and "we can delete you and show you your data" must be *demonstrated*, not asserted.
63+
64+
**Honest status note:** today the registry and lattice live in the engine library and its tests. Server-side read-time enforcement does not exist yet — the apps (`apps/api`, `apps/web`, `apps/mobile`) are **not wired to engine v2** and still implement the legacy v0 flow. The legacy v0 schema (`packages/shared/src/domain.js`) predates this posture; it is marked LEGACY and is not the v1 product surface. Until the wiring lands, the governance posture is a property of the engine, not of any deployed system.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!--
2+
SPDX-License-Identifier: MPL-2.0
3+
Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
-->
5+
# Design Decision Record
6+
7+
Summary of the four decisions made on **2026-06-11** after a five-lens survey (matching theory, market landscape, empirical harmony research, repo intent audit, adversarial engine critique). The full spec lives in-repo at [`docs/design/squad-audit-v1.adoc`](../blob/main/docs/design/squad-audit-v1.adoc) — that document is the product spec, and **where code and spec disagree, the spec wins until amended**.
8+
9+
## 1. User moment (v1): Squad Audit
10+
11+
An **already-formed friend group** gets a harmony + feasibility audit, an auto-drafted house agreement, and a search brief.
12+
13+
**Rationale.** UK structure creates an unserved moment: first-years are allocated halls by the university; by January they must form private-market groups of 3–6 with people they met eight weeks earlier, with no compatibility input. No incumbent serves this. SpareRoom owns listings liquidity, so flat-mate sits **upstream** of it — the output is a vetted household plus a search brief to take to the listings market. Crucially, Squad Audit **works at n=3 with zero marketplace liquidity**: no chicken-and-egg problem. Stranger discovery and solo-joins-household are deferred roadmap (see [[Theory-Foundations]] for why solo-joins-household is the one theory-clean centralized mechanism we can add later).
14+
15+
See [[Squad-Audit-Walkthrough]] for the flow end-to-end on real fixtures.
16+
17+
## 2. Data architecture: epistemic-typed server
18+
19+
VerisimDB persistence, ordinary Art. 6 UK GDPR controller duties accepted, **no Art. 9 (special-category) fields by schema construction**. Every stored field carries governance metadata `{purpose, revealingness, minTier}` enforced at read time.
20+
21+
**Rationale.** The cheapest special-category data problem is the one you structurally cannot have. The v1 schema removed `accessibilityNeeds` (health data; even a boolean indirectly reveals disability — CJEU C-184/20) and never stores guarantor status (a nationality proxy). Both are replaced by the *universal-prompt pattern*: the conversation is prompted unconditionally for every group, and the platform stores nothing. Details in [[Data-Governance]].
22+
23+
## 3. Disclosure model: conceal lattice with reciprocity
24+
25+
Tiers **T0 band / T1 relational / T2 full**; what A sees of B is `min(tierA, tierB)` (LinkedIn semantics — you may see at most what you yourself grant). Joining an audit group elects T2 within that group.
26+
27+
**Rationale.** Explainable matching output is itself personal data about *two* people; the lattice makes over-tier disclosure impossible rather than merely checked, and the reciprocity rule removes the asymmetric-stalker failure mode. The engine's implementation mirrors a formally verified Idris2 core in `proven-servers/protocols/proven-epistemic` where the lattice properties are machine-checked theorems. Details in [[Conceal-Lattice]].
28+
29+
## 4. Target wave: Jan–May 2027, London second years
30+
31+
One user moment, one university to seed, legal posture resolved by December 2026.
32+
33+
**Rationale.** The academic cycle gives the market its thickness for free (a Roth-style cohort window — see [[Theory-Foundations]]); London-first is an estate invariant; and a single seeded university makes the cold-start tractable for a product that needs no liquidity at all for its v1 moment.
34+
35+
## Architecture consequence: three layers
36+
37+
| Layer | Role | Status |
38+
|---|---|---|
39+
| Harmony engine | Predicts whether a set of people will live well together. Pure functions, explainable output. | Built — engine v2, see [[Engine-v2-Reference]]. |
40+
| Feasibility engine | PLACE constraint satisfaction: budget intersection, move-in window, lease length, household size vs HMO licensing. | v2 splits it out of the harmony score; grows toward commute/borough later. |
41+
| Market mechanisms | Sequenced by liquidity: (1) squad audit → (2) solo top-up discovery with conceal tiers → (3) DA rounds for solo-joins-household → (4) optional batch match days. | v1 ships only (1). |
42+
43+
Matching rules live in `packages/shared/src/matching/` and **never in UI components** — including the conceal/redaction layer, for the same reason (see [[Development]]).
44+
45+
**Honest status note:** the engine and its artifacts are built and tested; the apps (`apps/api`, `apps/web`, `apps/mobile`) are **not yet wired to engine v2** and still carry the legacy v0 swipe+listings flow.

0 commit comments

Comments
 (0)