HouseMatch COMPONENTS MAP

Section B — Flat Search

Every part of the running system, and what each one is handed, holds, does and returns. This describes what is actually deployed rather than an intended design: each row corresponds to code in the repository.

The load-bearing split. Eligibility is decided in deterministic code (public/assets/match.js). Gemini is used only at the edges — reading a typed answer at intake, and writing the tradeoff up afterwards. It is never asked whether a flat qualifies, so no non-negotiable can be paraphrased away.
Component Trigger Input Context Processing Output
RiyaCoordinator Decides to fix the process rather than hunt for another listing. Her own requirements, plus either the other two’s or a share link sent to them. Four months, zero shortlisted flats. She organises, but holds no veto. Completes the same form as the other two. Her coordinator role is a label on the summary and carries no weight in the matching. A confirmed profile, and either a group code or all three sets of requirements.
Meera & KavitaParticipants Open the share link, or answer in the bot, or have their requirements entered for them. Rent ceiling, deposit ceiling, ruled-out areas, building requirements, destinations with a maximum journey time, preferences. Meera: knee condition, so a lift is absolute and her budget is the flexible figure. Kavita: Hinjewadi office and the tightest ceiling in the group. Answer alone; neither sees the other’s answers until all three are in, which is enforced server-side rather than in the page. Requirements entered on their behalf are labelled as second-hand. Two more profiles, each played back for confirmation before it is saved.
Interfacepublic/app.html
api/telegram.js
Someone opens /app, or messages the bot. The eleven questions, as a web form or as a chat conversation. Two front doors onto one engine, so they can never disagree about whether a flat qualifies. Web: add people, run it, see the shortlist; matching runs in the browser, so it is instant and works offline. Telegram: /start, /add, /match, /why, with votes per option. The same shortlist either way.
Flat Search Apppublic/assets/match.js All requirements are in, or a run is asked for. Three profiles and the listing records. Equal three-way split; journeys checked at weekday office hours; missing fields treated as unknown. Splits the rent and checks each share against that person’s own ceiling; eliminates anything breaking a stated non-negotiable; scores preferences and travel; ranks on the worst-off person rather than the average. Two or three qualifying flats with a per-person breakdown — or an empty shortlist naming the limits that eliminated everything. Never a flat chosen for them.
GeminiLLM · lib/gemini.js
api/narrate.js
Someone types a travel answer in their own words, or a ranked option needs writing up. The raw sentence at intake; afterwards, the verdict the engine has already reached. The closed destination list, and each person’s passes, failures and open questions. Reads a typed answer into a structured suggestion, asking rather than guessing when no time limit was given. Writes the tradeoff in plain English. Never decides. It cannot see the filtering and nothing it returns re-enters eligibility. A clarifying question, or two or three sentences naming who is compromising on what. Called server-side so the key never reaches a browser, and returned after the shortlist is already on screen, so a slow or missing model delays nothing. Without a key the wording falls back to deterministic text and matching is unaffected.
Property APIpublic/data/listings.js A match run begins. Area, address, BHK, rent, deposit, built-up area, furnishing, preferred tenants, availability. Scraped live from NoBroker on 27 Sep 2026; the RapidAPI wrapper does not work, so the rendered search page was read per area. The raw scrape is committed verbatim and normalised in visible code. Preserves missing fields as null rather than defaulting them. Lift, parking, bathrooms and maintenance are unstated for all 55 records, so absent data can never satisfy a requirement. Area labels contradicted by their own address are flagged, and travel checks on those records are downgraded rather than trusted. 55 real records across 6 Pune areas, each with its own NoBroker link so any row can be checked by hand.
SupabaseDatabase · lib/store.js
api/group.js
Somebody joins a shared group, answers a question, or votes on an option. Profiles keyed by group code, in-progress form state, and interested / discuss / reject votes. Serverless functions keep nothing between requests, so all shared state lives here. Row level security is on with no public policy, so the anon key can read nothing. Reads and writes over PostgREST using the service role. The seal is enforced here: a browser asking for the full profile set before everyone is in is refused. In the page that check would be readable from the network tab, and seeing the others first is how people end up tailoring their own answers to somebody else’s. Durable profiles, share links across devices, and a vote tally. Without it the web app still works for one person entering all three sets, and says so.
Other APIOptional · public/data/travel.js A listing has to be tested against somebody’s travel limit. The listing’s area, the destination, and that person’s maximum minutes. Kavita’s office run to Hinjewadi; Riya’s 25-minute radius to her family and her gym. Compares a journey range against the limit: upper bound inside it passes, lower bound already past it fails, and a range straddling the limit returns needs verification rather than being rounded to a midpoint and called a match. A pass, a fail or an open question per person per destination. These are area-level planning estimates; a live routing API would drop in here unchanged.

What the system will not do

Choose

It ranks and explains. The decision stays with the three of them, which is the whole point of handing them options with the compromises written down.

Quietly relax a dealbreaker

If nothing qualifies it says so and names the limits doing the eliminating. Filling the shortlist by softening somebody’s requirement would recreate the exact failure this replaces.

Treat missing data as a pass

An unstated lift is an open question attached to the option, never a satisfied requirement. Nobody is told a dealbreaker is met on the strength of a blank field.