Meshi Platform — System Overview
Meshi Platform — System Overview
Meshi is a people-intelligence platform that builds structured, evolving profiles of individuals from diverse data sources. It provides a backend for portable professional identity, contextual matching, and AI-guided enrichment, built on a trait-claim architecture with explicit trust and provenance at every layer.
Read this document first. It is the mental model you need before any other doc makes sense.
What Meshi does
Meshi takes raw identity signals — LinkedIn profiles, event attendance data, uploaded CSVs, conversation — and converts them into structured, queryable, high-trust profiles. Those profiles feed:
- Matchmaking: rank members by goal complementarity and value alignment
- Coach: an AI coach that drives goal-setting and profile refinement
- Onboarding: a guided kickoff flow that bootstraps a member’s profile on first join
- Organizer tooling: bulk person import for events, with canonical identity deduplication
The product vision is a professional network where the platform knows enough about each member to make genuinely useful introductions, not just mutual-connection graphs.
Core concepts
Six abstractions underpin everything. Understand these before reading any technical doc.
Entity and Person
An entity is the universal identity root. Every person, organization, or event is an entity first. A person is a typed extension of an entity — it carries factual attributes like display name, role, company, and LinkedIn URL. All other data (traits, evidence, embeddings) hangs off the entity.
Source Records
A source record is an immutable snapshot of raw evidence: a LinkedIn profile as it appeared on a given date, a CSV row from an event import, a manual form submission. Everything in Meshi is traceable back to a source record. Provenance is non-negotiable.
Traits and Claims
A trait is an atomic statement about an entity — a specific skill, value, interest, or
characteristic. Traits live in a trait_dimension taxonomy. Each assertion is stored as a
trait_claim row with a status state machine — proposed, confirmed, rejected,
superseded, archived — defined in packages/domain/src/types.ts (TraitClaimStatus) and
governed by packages/domain/src/state-machine.ts. Claims from high-authority sources (e.g. direct
user confirmation) override lower-authority inferences. Consumer code uses trust profiles to
decide which claims to read, so the same entity can appear differently to a matchmaking engine vs. a
public-facing brief.
Goals, Needs, and Offers
Goals are first-class entities representing a member’s professional intent. A goal has a type (e.g. “find a co-founder”, “hire an engineer”) and derives structured Needs and Offers via LLM extraction. Matchmaking scores pairs on how well candidate Offers cover principal Needs and vice versa.
The Brief
The canonical brief is a narrated, synthesized summary of an entity’s profile — it is a derived artifact, not a source of truth. The brief synthesis worker runs after trait inference settles and produces the prose that feeds the coach context, public profile displays, and embedding generation.
The Pipeline
The pipeline is the entity’s progress through enrichment and inference. It is tracked as a state
machine on the person_pipeline table whose canonical stage list lives in
packages/domain/src/pipeline-stages.ts. Active stages: queued, linkedin_searching,
linkedin_matched, linkedin_review, enriching, web_researching, research_review,
inferring, synthesizing. Terminal stages: complete, complete_degraded, failed, skipped,
unresearchable, inference_failed. The set of allowed transitions is also exported from that
module and seeded into a pipeline_allowed_transitions table — a CI test asserts the DB matches the
constant exactly. Pipeline transitions use optimistic concurrency (CAS) so concurrent workers don’t
clobber each other.
System architecture
Data flows through five layers in sequence. Each layer feeds the next via Inngest events carried through a durable outbox.
┌─────────────────────────────────────────────────────────┐│ 1. INGESTION ││ CSV / LinkedIn / form → source_record → entity link │└────────────────────────┬────────────────────────────────┘ │ LINKEDIN_PROFILE_FETCHED │ PREFLIGHT_ENRICHMENT_REQUESTED ▼┌─────────────────────────────────────────────────────────┐│ 2. ENRICHMENT ││ Apify scrape → timeline records → text chunks ││ Deep research → web evidence → source chunks │└────────────────────────┬────────────────────────────────┘ │ EVIDENCE_ENRICHMENT_COMPLETED │ ENRICHMENT_COMPLETED ▼┌─────────────────────────────────────────────────────────┐│ 3. INFERENCE ││ Trait inference → trait_claim rows (proposed) ││ Goal inference → goal rows + needs/offers ││ Brief synthesis → canonical_brief ││ Embedding → vector(1024) for ANN search │└────────────────────────┬────────────────────────────────┘ │ BRIEF_SYNTHESIZED │ pipeline stage → complete ▼┌─────────────────────────────────────────────────────────┐│ 4. FEATURES ││ Matchmaking (goal-centric ANN + scoring) ││ Coach runtime (MCP + AI agent loop) ││ Onboarding kickoff (stations + state machine) │└─────────────────────────────────────────────────────────┘
At every layer: domain_event_outbox → relay → Inngest Guarantees at-least-once delivery and replay on failure.Outbox relay
All material state changes write a row to domain_event_outbox before returning. A background relay
process reads undelivered rows and pushes them to Inngest. The outbox is the only place events
originate — no fire-and-forget direct Inngest calls. This means any failure can be replayed by
re-running the relay.
Key features
| Feature | What it does |
|---|---|
| Onboarding (kickoff) | Multi-station flow (Portrait → Capabilities → Goals → Read → Pact) that builds a high-trust initial profile from LinkedIn or conversation |
| Matchmaking | Scores member pairs by Goal-Complementarity (55%), Value-Alignment (25%), General-Similarity (20%); uses ANN prefiltering with 60s result caching |
| Deep research | Autonomous web-discovery pipeline that searches, fetches, and evaluates pages to enrich a member’s evidence base |
| Coach runtime | AI agent loop (TypeScript MCP runtime) connected to Meshi tools; drives goal-setting conversations and profile refinement |
| Person import | Bulk-ingest event attendee lists (CSV/LinkedIn) while maintaining canonical entity deduplication across imports |
Monorepo layout
| Package | What it does |
|---|---|
packages/domain | Pure TypeScript domain types, state machines, normalization, trust profiles. Zero internal deps — the leaf everyone else depends on. |
packages/db | Database connection, all SQL migrations (Kysely), and repository functions. All schema changes go here. |
packages/core | Business logic service layer — shared by the API, MCP server, and workers. No HTTP, no Inngest. |
packages/api | Hono HTTP API, Better Auth integration, all routes. Mounts MCP and Inngest handlers at the composition root (main.ts). |
packages/workers | Inngest functions — enrichment, inference, brief synthesis, embedding, outbox relay. All async pipeline logic lives here. |
packages/mcp | MCP server — tool registrations giving AI agents access to Meshi services over Streamable HTTP. |
tools/ | Developer utilities (Kysely type generation, etc.). |
Dependency rule: domain → db → core → api / workers / mcp. No package may depend on
api or workers. Integration tests are the only place cross-package imports are allowed.
Where to go next
| Goal | Read |
|---|---|
| Understand the full identity and trust doctrine | meshi-v0.1-spec.md (repo root) |
| Understand the onboarding flow end-to-end | onboarding-flow.md |
| Understand how matchmaking works | matchmaking-v0-spec.md |
| Understand the enrichment/research pipeline | deep-research-pipeline.md |
| Understand the event architecture (UEG) | ueg-agent-ready-events-architecture.md |
| Deploy or debug the coach runtime | coach-runtime-mcp-deploy.md |
| Understand repo commands, conventions, testing rules | AGENTS.md (repo root) |
| Find validated code patterns | PATTERNS.md (repo root) |