navidocs/docs/debates/01-schema-and-vertical-analysis.md
ggq-admin c54c20c7af docs: Add expert panel debates on schema design and vertical analysis
- Tech panel: Database schema, Meilisearch config, future-proofing
- Boating vertical: Domain experts on boat documentation needs
- Property/Marina vertical: Multi-entity hierarchy and compliance
- Cross-vertical pattern analysis: Unified schema for all use cases

Consensus: Search-first architecture with SQLite + Meilisearch hybrid
2025-10-19 01:20:17 +02:00

1412 lines
38 KiB
Markdown

# Expert Panel Debates: NaviDocs Architecture & Vertical Analysis
---
## PANEL 1: TECH ARCHITECTURE & SCHEMA DESIGN
**Duration:** 47 minutes of intense debate
**Attendees:**
- **Database Architect** (PostgreSQL partisan, hates breaking changes)
- **Search Engineer** (Meilisearch specialist, schema pragmatist)
- **Backend Lead** (Node.js, API-first thinking)
- **Data Scientist** (ML/embeddings future-gazer)
- **DevOps** (migration nightmares survivor)
---
### Round 1: Core Schema Philosophy
**Database Architect:** "We need to talk about the elephant in the room. SQLite is fine for prototyping, but you're building a multi-tenant document management system. What happens when user #500 uploads a 200-page manual and you need to query across 50,000 indexed pages? SQLite will choke. We should start with Postgres + proper normalization."
**Backend Lead:** "Hard disagree. Premature optimization. We have ZERO users. SQLite handles 100GB+ databases fine with proper indexing. The real question is: do we normalize or go document-oriented?"
**Search Engineer:** "You're both missing the point. The source of truth IS Meilisearch for this use case. Think about it - every query is a search query. 'Show me all electrical manuals for my boat' - that's a search with filters, not a SQL join. The relational DB is just a durable write log."
**Database Architect:** "That's insane. Meilisearch is a search index, not a database. What about transactions? ACID guarantees? Backups?"
**Search Engineer:** "SQLite gives us ACID. Meilisearch gives us instant search. We use both, but design the schema for search-first, not relational-first."
**Data Scientist:** "Can I throw a grenade in here? What about vector embeddings? If we're doing semantic search on boat manuals - 'how do I fix the bilge pump' should match documents that never use the word 'bilge' - we need embedding vectors. That's 1536 floats per chunk. SQLite can't index that. Postgres with pgvector can."
**DevOps:** "Let me tell you about my last migration hell: changing a column type in production with 10M rows. Took 6 hours of downtime. Whatever schema we pick, it MUST support additive changes only. No column renames, no type changes, no foreign key reshuffling."
**Backend Lead:** "Okay, consensus check: Hybrid approach. SQLite for metadata (user, boat, document). Meilisearch for search. Future: add pgvector when we need semantic search. Schema must be append-only friendly."
**Database Architect:** "Fine, but the schema has to be designed for eventual Postgres migration. That means:"
- Primary keys are UUIDs (portable)
- Timestamps are ISO8601 strings or Unix epochs (no SQLite DateTime gotchas)
- JSON columns are minimal (no nested objects we'll regret)
- Foreign keys are explicit even if not enforced
**Search Engineer:** "And Meilisearch schema must have:"
- Filterable attributes defined upfront (boatId, system, category)
- Sortable attributes (createdAt, updatedAt, pageNumber)
- Searchable attributes weighted (title: 3, text: 1, metadata: 0.5)
- Synonyms configured from day one
---
### Round 2: Schema Design - The Great Debate
**Database Architect:** "Let me propose the relational schema:"
```sql
-- Users table
CREATE TABLE users (
id TEXT PRIMARY KEY, -- UUID
email TEXT UNIQUE NOT NULL,
name TEXT,
created_at INTEGER NOT NULL, -- Unix timestamp
updated_at INTEGER NOT NULL
);
-- Boats table (multi-boat support from day 1)
CREATE TABLE boats (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
name TEXT NOT NULL,
make TEXT,
model TEXT,
year INTEGER,
hull_id TEXT, -- Official hull identification number
metadata TEXT, -- JSON for extensibility
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Documents table
CREATE TABLE documents (
id TEXT PRIMARY KEY,
boat_id TEXT NOT NULL,
title TEXT NOT NULL,
file_path TEXT NOT NULL,
file_size INTEGER NOT NULL,
page_count INTEGER,
mime_type TEXT,
uploaded_by TEXT NOT NULL,
status TEXT DEFAULT 'processing', -- processing, indexed, failed
metadata TEXT, -- JSON: {system, category, tags, etc}
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (boat_id) REFERENCES boats(id) ON DELETE CASCADE,
FOREIGN KEY (uploaded_by) REFERENCES users(id)
);
-- Pages table (for OCR results)
CREATE TABLE pages (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL,
page_number INTEGER NOT NULL,
text TEXT, -- OCR extracted text
ocr_confidence REAL,
metadata TEXT, -- JSON: bounding boxes, etc
created_at INTEGER NOT NULL,
FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE,
UNIQUE(document_id, page_number)
);
-- Sharing table (future: share manuals with crew)
CREATE TABLE shares (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL,
shared_by TEXT NOT NULL,
shared_with TEXT NOT NULL,
permission TEXT DEFAULT 'read', -- read, write, admin
created_at INTEGER NOT NULL,
FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE,
FOREIGN KEY (shared_by) REFERENCES users(id),
FOREIGN KEY (shared_with) REFERENCES users(id)
);
```
**Backend Lead:** "Wait wait wait. Why is `pages` a separate table? That's going to be millions of rows. Every search requires a join. Bad idea."
**Database Architect:** "Because we need to track per-page metadata! OCR confidence, bounding boxes for highlighting, page-level annotations later. It's normalized."
**Search Engineer:** "This is exactly why I said search-first. In Meilisearch, we don't JOIN. Each page is a document:"
```json
{
"id": "page_doc123_p7",
"docId": "doc123",
"boatId": "boat_prestige_f49",
"userId": "user_456",
"title": "8.7 Blackwater System",
"pageNumber": 7,
"text": "The blackwater pump is located in the aft compartment...",
"system": "plumbing",
"category": "waste-management",
"make": "Prestige",
"model": "F4.9",
"year": 2024,
"tags": ["bilge", "pump", "maintenance"],
"ocrConfidence": 0.94,
"createdAt": 1740234567,
"filePath": "/manuals/doc123.pdf"
}
```
**Backend Lead:** "Okay, that's actually elegant. One Meilisearch query gets everything. But we still need the relational DB for:"
- User authentication
- Boat ownership
- File metadata
- Audit logs
**Data Scientist:** "And here's where it gets interesting. When we add semantic search:"
```json
{
"id": "page_doc123_p7",
// ... all the above fields ...
"embedding": [0.023, -0.154, 0.672, ...], // 1536 floats
"_vectors": {
"default": [0.023, -0.154, ...]
}
}
```
"Meilisearch doesn't support vector search natively yet. We'll need a hybrid approach: Meilisearch for keyword + filters, separate vector DB (Qdrant/Weaviate) for semantic search, merge results. But the schema MUST have an embedding field from day 1, even if it's null initially."
**DevOps:** "Migration strategy question: how do we version this? What if we realize next month that 'system' should be an array, not a string? A manual can cover electrical AND plumbing."
**Database Architect:** "That's why metadata is JSON. We can evolve it without schema changes."
**Search Engineer:** "Wrong. JSON is a cop-out. If 'system' can be multiple values, make it an array in Meilisearch from day 1:"
```json
{
"systems": ["electrical", "plumbing"], // filterable array
"categories": ["maintenance", "troubleshooting"]
}
```
**Backend Lead:** "Okay, new proposal. Hybrid schema:"
**SQLite (source of truth for entities):**
```sql
CREATE TABLE documents (
id TEXT PRIMARY KEY,
boat_id TEXT NOT NULL,
title TEXT NOT NULL,
file_path TEXT NOT NULL,
status TEXT DEFAULT 'processing',
metadata TEXT, -- Extensible JSON
created_at INTEGER NOT NULL
);
CREATE TABLE document_pages (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL,
page_number INTEGER NOT NULL,
ocr_text TEXT,
ocr_confidence REAL,
search_indexed BOOLEAN DEFAULT 0,
created_at INTEGER NOT NULL
);
```
**Meilisearch (optimized for search):**
```json
{
"id": "page_doc123_p7",
"docId": "doc123",
"boatId": "boat_abc",
"userId": "user_xyz",
"title": "Document Title",
"pageNumber": 7,
"text": "Full OCR text...",
"systems": ["electrical", "plumbing"],
"categories": ["maintenance"],
"make": "Prestige",
"model": "F4.9",
"year": 2024,
"tags": ["bilge", "pump"],
"createdAt": 1740234567
}
```
**All:** "Agreed."
---
### Round 3: Future-Proofing - The Breaking Changes Discussion
**DevOps:** "Let's game out the disasters:"
**Scenario 1:** User wants to reorganize their manuals. Change boat assignment.
**Database Architect:**
```sql
-- Good: Just update boat_id
UPDATE documents SET boat_id = 'new_boat_id' WHERE id = 'doc123';
-- Then re-index in Meilisearch
-- No schema change needed ✅
```
**Scenario 2:** We add "vessel type" field (sailboat vs powerboat).
**Backend Lead:**
```sql
-- SQLite: Add column (additive change)
ALTER TABLE boats ADD COLUMN vessel_type TEXT;
-- Meilisearch: Add to index settings (non-breaking)
{
"filterableAttributes": ["boatId", "system", "vesselType"], // Added vesselType
// Existing docs without vesselType still searchable
}
```
**Search Engineer:** "Key point: Meilisearch handles missing fields gracefully. We can add 'vesselType' today, and old documents won't break. They just won't match `vesselType = 'sailboat'` filters."
**Scenario 3:** We realize 'system' should be hierarchical (electrical → navigation → GPS).
**Data Scientist:** "This is where most schemas fail. Don't fight it - use a path:"
```json
{
"systemPath": "electrical.navigation.gps",
"systemHierarchy": ["electrical", "navigation", "gps"],
// Both: path for display, array for filtering
}
```
**Backend Lead:** "Wait, that's duplication."
**Search Engineer:** "It's not duplication, it's denormalization for query performance. Filtering on `systemHierarchy IN ['electrical']` matches all electrical subdocuments. Denormalization is correct for search indexes."
**Scenario 4:** We add multi-language support (French boat manuals).
**Database Architect:**
```sql
-- SQLite
ALTER TABLE documents ADD COLUMN language TEXT DEFAULT 'en';
-- Meilisearch
{
"id": "page_doc123_p7_en",
"language": "en",
// ...
}
{
"id": "page_doc123_p7_fr",
"language": "fr",
"text": "La pompe des eaux noires est située...",
// ...
}
```
**Search Engineer:** "Meilisearch has language-specific stemmers. We'd create separate indexes per language for optimal search."
**Data Scientist:** "Or, use language-agnostic embeddings. One index, multilingual semantic search."
**Scenario 5:** User uploads a new version of a manual (manufacturer releases updated manual).
**DevOps:** "This is the BIG ONE. Do we version documents?"
**Database Architect:**
```sql
CREATE TABLE documents (
id TEXT PRIMARY KEY,
boat_id TEXT NOT NULL,
version INTEGER DEFAULT 1,
parent_version_id TEXT, -- Points to previous version
is_current BOOLEAN DEFAULT 1,
// ...
);
```
**Backend Lead:** "That's complicated. Simpler: treat new upload as new document, mark old one as 'archived':"
```sql
CREATE TABLE documents (
id TEXT PRIMARY KEY,
boat_id TEXT NOT NULL,
status TEXT DEFAULT 'active', -- active, archived, deleted
replaced_by TEXT, -- Points to newer version
// ...
);
```
**All:** "Simpler approach wins."
---
### Round 4: Synonyms & Search Intelligence
**Search Engineer:** "Let's talk synonyms. Boating has specific terminology:"
```json
{
"bilge pump": ["bilge", "sump pump", "drain pump"],
"head": ["toilet", "marine toilet", "WC"],
"galley": ["kitchen"],
"helm": ["steering", "wheel", "cockpit controls"],
"bow": ["front", "forward"],
"stern": ["aft", "back", "rear"],
"port": ["left"],
"starboard": ["right"],
"VHF": ["radio", "marine radio"],
"GPS": ["chartplotter", "navigation system"],
"autopilot": ["auto helm"],
"windlass": ["anchor winch"],
"thruster": ["bow thruster", "stern thruster"]
}
```
**Data Scientist:** "This is where embeddings shine. 'My head is clogged' should match toilet troubleshooting WITHOUT explicit synonyms. But for V1, synonyms are good enough."
**Search Engineer:** "Key config:"
```javascript
// Meilisearch settings
{
"searchableAttributes": [
"title",
"text",
"systems",
"categories",
"tags"
],
"filterableAttributes": [
"boatId",
"userId",
"docId",
"systems",
"categories",
"make",
"model",
"year",
"language"
],
"sortableAttributes": [
"createdAt",
"pageNumber",
"year"
],
"synonyms": {
"bilge": ["sump", "drain"],
"head": ["toilet", "marine toilet"],
// ... all synonyms
},
"stopWords": ["the", "a", "an", "and", "or"],
"rankingRules": [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness"
],
"typoTolerance": {
"enabled": true,
"minWordSizeForTypos": {
"oneTypo": 4,
"twoTypos": 8
}
}
}
```
**Backend Lead:** "Question: how do we update synonyms without breaking existing searches?"
**Search Engineer:** "Synonyms are index settings, not data. We can update them anytime without re-indexing. Just restart Meilisearch with new config."
---
### CONSENSUS: Final Tech Schema
**Database Architect:** "Okay, here's what we agreed on:"
**SQLite Schema (v1.0):**
```sql
-- Core entities
CREATE TABLE users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE boats (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
name TEXT NOT NULL,
make TEXT,
model TEXT,
year INTEGER,
hull_id TEXT,
vessel_type TEXT, -- sailboat, powerboat, catamaran, etc
length_feet INTEGER,
metadata TEXT, -- JSON for extensibility
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE documents (
id TEXT PRIMARY KEY,
boat_id TEXT NOT NULL,
user_id TEXT NOT NULL,
title TEXT NOT NULL,
file_path TEXT NOT NULL,
file_size INTEGER NOT NULL,
file_hash TEXT NOT NULL, -- SHA256 for deduplication
page_count INTEGER,
mime_type TEXT DEFAULT 'application/pdf',
language TEXT DEFAULT 'en',
status TEXT DEFAULT 'processing', -- processing, indexed, failed, archived
replaced_by TEXT, -- Document ID that supersedes this
metadata TEXT, -- JSON
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE document_pages (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL,
page_number INTEGER NOT NULL,
ocr_text TEXT,
ocr_confidence REAL,
ocr_completed_at INTEGER,
search_indexed_at INTEGER,
created_at INTEGER NOT NULL,
UNIQUE(document_id, page_number)
);
CREATE TABLE ocr_jobs (
id TEXT PRIMARY KEY,
document_id TEXT NOT NULL,
status TEXT DEFAULT 'pending', -- pending, processing, completed, failed
progress INTEGER DEFAULT 0, -- 0-100
error TEXT,
started_at INTEGER,
completed_at INTEGER,
created_at INTEGER NOT NULL
);
-- Indexes
CREATE INDEX idx_boats_user ON boats(user_id);
CREATE INDEX idx_documents_boat ON documents(boat_id);
CREATE INDEX idx_documents_status ON documents(status);
CREATE INDEX idx_pages_document ON document_pages(document_id);
CREATE INDEX idx_jobs_status ON ocr_jobs(status);
```
**Meilisearch Schema (v1.0):**
```javascript
// Index: boat-manuals-pages
{
// Primary fields
"id": "page_{docId}_p{pageNum}",
"docId": "doc_abc123",
"boatId": "boat_xyz789",
"userId": "user_456",
// Display fields
"title": "Owner's Manual - Section 8.7",
"pageNumber": 7,
"text": "Full OCR extracted text...",
// Classification (arrays for multi-tagging)
"systems": ["electrical", "navigation"],
"categories": ["maintenance", "troubleshooting"],
"tags": ["bilge", "pump", "replacement"],
// Boat metadata (denormalized for filtering)
"boatName": "Sea Breeze",
"make": "Prestige",
"model": "F4.9",
"year": 2024,
"vesselType": "powerboat",
// Document metadata
"language": "en",
"ocrConfidence": 0.94,
// Timestamps (Unix)
"createdAt": 1740234567,
"updatedAt": 1740234567,
// Future: embeddings
"embedding": null // Will be array of 1536 floats
}
// Meilisearch Settings
{
"searchableAttributes": [
"title",
"text",
"systems",
"categories",
"tags",
"boatName"
],
"filterableAttributes": [
"boatId",
"userId",
"docId",
"systems",
"categories",
"make",
"model",
"year",
"vesselType",
"language"
],
"sortableAttributes": [
"createdAt",
"updatedAt",
"pageNumber",
"year",
"ocrConfidence"
],
"synonyms": { /* boat terminology */ },
"stopWords": ["the", "a", "an"],
"typoTolerance": { "enabled": true }
}
```
**All:** "Schema approved. Next panel."
---
## PANEL 2: BOATING VERTICAL - DOMAIN EXPERTS
**Duration:** 38 minutes
**Attendees:**
- **Marine Surveyor** (30 years inspecting boats)
- **Boat Manufacturer Rep** (knows what docs matter)
- **Marina Manager** (handles 200+ boats)
- **Sailing Instructor** (teaches boat systems)
- **Yacht Broker** (deals with documentation nightmares)
---
### Round 1: What Documents Actually Matter?
**Marina Manager:** "Let me tell you what I deal with daily. Boat owners show up and say 'where's the manual for the water heater?' They have NO IDEA. Is it in the owner's manual? Service manual? A separate brochure? It's chaos."
**Marine Surveyor:** "Here's the taxonomy that actually matters in real life:"
```
1. VESSEL DOCUMENTATION
- Builder's certificate
- Registration papers
- Insurance documents
- Survey reports
- Coast Guard documentation
2. OWNER'S MANUAL (the big book)
- Hull & construction
- Systems overview
- Safety equipment
- Specifications
3. SYSTEMS MANUALS (the critical ones)
PROPULSION:
- Engine manual (Volvo, Yanmar, Caterpillar)
- Transmission/drive
- Propeller specs
ELECTRICAL:
- Generator manual
- Shore power system
- Battery systems
- Solar/wind if equipped
PLUMBING:
- Fresh water system
- Waste water (grey/black)
- Bilge pumps
- Water heater
- Desalination (if equipped)
NAVIGATION:
- Chartplotter
- Radar
- AIS
- VHF radio
- Autopilot
HVAC:
- Air conditioning
- Heating system
SAFETY:
- Life raft service manual
- EPIRB
- Fire suppression
4. COMPONENT MANUALS
- Every. Single. Thing.
- Refrigerator, stove, microwave
- Windlass, davit, watermaker
- Entertainment systems
5. SERVICE RECORDS
- Maintenance logs
- Repair invoices
- Part receipts
```
**Boat Manufacturer Rep:** "The problem is we give owners a 400-page PDF. They never find what they need. When they call us: 'How do I winterize the water system?' - it's on page 287, section 12.4.6. They'll never find it."
**Sailing Instructor:** "What boat owners ACTUALLY search for:"
```
- "How do I start the engine?" (page 45)
- "Bilge pump not working" (page 198)
- "Water heater troubleshooting" (page 215)
- "Generator won't start" (page 167)
- "How to use the autopilot" (page 312)
- "Winterization procedure" (page 287)
- "Battery charging system" (page 134)
```
**Yacht Broker:** "Here's the nightmare: I'm selling a 2019 Prestige F4.9. Buyer asks: 'Does it have the original manuals?' Owner says: 'Yeah, somewhere...' They can't find them. I lose the sale. OR worse, they find photocopies from 3 different boats mixed together."
**Marine Surveyor:** "This is why the schema MUST support:"
```javascript
{
"documentType": "component-manual", // vs owner-manual, service-record, etc
"component": "water-heater",
"manufacturer": "Webasto",
"modelNumber": "FCF Platinum Series",
"serialNumber": "WB-2024-12345", // Critical for warranty claims
"installDate": "2024-03-15",
"warrantyExpires": "2026-03-15"
}
```
**Marina Manager:** "And here's the killer feature nobody thinks of: SHARED MANUALS. Ten boats have the same Volvo D4 engine. I should upload the Volvo D4 manual ONCE, and all ten boats reference it. Not ten copies."
**All:** "That's brilliant. Shared component library."
---
### Round 2: Boat-Specific Classification
**Marine Surveyor:** "The 'systems' classification we discussed is good, but it needs to match how boat owners think:"
```javascript
// WRONG (too technical)
{
"systems": ["hvac", "thermal-management"]
}
// RIGHT (how owners talk)
{
"systems": ["air-conditioning", "heating"],
"synonyms": ["AC", "climate control", "heat pump"]
}
```
**Sailing Instructor:** "Also, sailboats vs powerboats have different priorities:"
```javascript
// Sailboat-specific
{
"systems": [
"rigging",
"sail-handling",
"keel",
"rudder",
"winches"
]
}
// Powerboat-specific
{
"systems": [
"trim-tabs",
"joystick-control",
"stabilizers",
"bow-thruster",
"stern-thruster"
]
}
```
**Boat Manufacturer Rep:** "And size matters. A 30-foot boat owner doesn't care about 'tender garage' or 'crew quarters'. A 70-foot yacht owner does:"
```javascript
{
"vesselSize": "70ft",
"luxuryFeatures": [
"tender-garage",
"crew-quarters",
"wine-cooler",
"washer-dryer"
]
}
```
**Yacht Broker:** "For resale value, we MUST track:"
```javascript
{
"maintenanceHistory": [
{
"date": "2024-08-15",
"type": "service",
"system": "engine",
"description": "500-hour service - Volvo D4",
"serviceProvider": "ABC Marine",
"cost": 2400,
"documentId": "receipt_abc123"
}
]
}
```
---
### Round 3: Emergency Scenarios
**Marine Surveyor:** "The app must work OFFLINE. You're 20 miles offshore. Engine warning light comes on. You need the manual NOW. No cell signal."
**All:** "PWA with offline cache. Critical manuals pre-cached."
**Marina Manager:** "And the search needs to understand urgency:"
```javascript
// User searches: "bilge alarm"
// System should prioritize:
{
"results": [
{
"title": "Emergency: Bilge Alarm Troubleshooting",
"priority": "critical",
"quickFix": "Check bilge pump circuit breaker (panel C, position 12)"
},
// Then detailed manual sections
]
}
```
**Sailing Instructor:** "Context-aware search. If I search 'anchor' while moving at 6 knots, show me 'deploying anchor'. If I'm stationary, show me 'anchor windlass troubleshooting'."
**Boat Manufacturer Rep:** "That's too complex for V1. But bookmarks are essential:"
```javascript
{
"bookmarks": [
{
"pageId": "page_doc123_p45",
"label": "How to start engine",
"quickAccess": true
}
]
}
```
---
### CONSENSUS: Boating Schema Additions
```javascript
// Extended Meilisearch document
{
// ... base fields ...
// Boating-specific
"documentType": "owner-manual" | "component-manual" | "service-record" | "safety-certificate",
"component": "engine" | "generator" | "watermaker" | etc,
"manufacturer": "Volvo Penta",
"modelNumber": "D4-300",
"serialNumber": "VP-2024-12345",
// Vessel classification
"vesselType": "powerboat" | "sailboat" | "catamaran" | "trawler",
"vesselSize": 49, // feet
// Emergency priority
"priority": "critical" | "normal" | "reference",
// Offline support
"offlineCache": true, // Should be cached for offline
// Shared components
"sharedComponentId": "comp_volvo_d4_300", // Multiple boats reference same manual
// Maintenance tracking
"lastServiceDate": 1740234567,
"nextServiceDue": 1750234567,
"serviceIntervalHours": 500
}
```
---
## PANEL 3: PROPERTY/MARINA VERTICAL
**Duration:** 29 minutes
**Attendees:**
- **Commercial Real Estate Broker** (marinas, boat storage)
- **Marina Operations Manager** (infrastructure docs)
- **Marine Construction Engineer** (dock/pier systems)
- **Property Manager** (waterfront properties)
---
### Round 1: The Marina Documentation Problem
**Marina Operations Manager:** "We have 200 boat slips. Each slip has:"
```
- Electrical hookup (30A or 50A)
- Water connection
- Dock box
- Slip number sign
- WiFi repeater
```
"Every single one has manuals. Electrical panel? Manual. WiFi system? Manual. Dock construction specs? Manual. Fire suppression? Manual. It's the same problem as boat owners, but 100x scale."
**Marine Construction Engineer:** "And here's the thing: marinas have INFRASTRUCTURE documentation:"
```
MARINA INFRASTRUCTURE DOCS:
1. Dock Construction
- Pile installation specs
- Decking materials
- Load capacity per slip
- Seismic/storm ratings
2. Utilities
- Electrical distribution (main panels, pedestals)
- Water supply (pressure, filtration)
- Sewage pump-out system
- Fuel dock system (storage, pumps, leak detection)
3. Safety Systems
- Fire suppression
- Security cameras
- Lighting
- Emergency call boxes
4. Buildings
- Clubhouse HVAC
- Restroom/shower facilities
- Ship store
- Restaurant/bar equipment
5. Compliance
- Environmental permits
- Coast Guard inspections
- ADA compliance docs
- Insurance certificates
```
**Commercial Real Estate Broker:** "When I'm listing a marina for sale, buyers want to see ALL of this. Organized. Current. Not 'somewhere in the harbormaster's office filing cabinet'."
**Property Manager:** "Same with waterfront condos. Each unit might have a boat slip. HOA needs to track:"
```javascript
{
"propertyType": "waterfront-condo",
"unit": "Building A, Unit 305",
"assignedSlip": "A-42",
"slipDocuments": [
"electrical-inspection-2024.pdf",
"slip-assignment-agreement.pdf",
"dock-insurance-policy.pdf"
],
"buildingDocuments": [
"hvac-system-manual.pdf",
"elevator-maintenance-log.pdf",
"fire-system-inspection.pdf"
]
}
```
---
### Round 2: Multi-Entity Hierarchy
**Marina Operations Manager:** "Here's where it gets complex. Our marina is owned by XYZ Corp. XYZ Corp owns 5 marinas. Each marina has different systems:"
```
XYZ Corporation
├── Marina A (San Diego)
│ ├── Dock 1 (slips 1-40)
│ ├── Dock 2 (slips 41-80)
│ ├── Fuel Dock
│ ├── Clubhouse
│ └── Boatyard (repairs)
├── Marina B (Newport Beach)
│ └── ...
└── Marina C (Long Beach)
└── ...
```
**Commercial Real Estate Broker:** "And when they sell Marina A, the new owner needs ALL documentation for ONLY Marina A. Not the whole corporation."
**Property Manager:** "This is a permission nightmare. Who can see what?"
```javascript
// User roles
{
"userId": "john_marina_manager",
"role": "marina-manager",
"scope": {
"organization": "xyz-corp",
"marinas": ["marina-a"], // Can only see Marina A docs
"permissions": ["read", "write", "share"]
}
}
{
"userId": "corporate_admin",
"role": "org-admin",
"scope": {
"organization": "xyz-corp",
"marinas": ["marina-a", "marina-b", "marina-c"], // All marinas
"permissions": ["read", "write", "share", "delete", "admin"]
}
}
```
---
### Round 3: Property Schema
**Marine Construction Engineer:** "The schema needs to support hierarchy:"
```javascript
// Meilisearch document for property/marina
{
"id": "doc_marina_a_dock1_electrical",
// Hierarchy
"organizationId": "xyz-corp",
"propertyId": "marina-a",
"facilityId": "dock-1",
"componentId": "electrical-panel-3",
// Document details
"documentType": "infrastructure-manual",
"title": "Dock 1 Electrical Panel #3 - Installation Manual",
"system": "electrical",
"category": "infrastructure",
// Access control (filterable)
"visibility": "marina-a", // Only visible to marina-a users
// Compliance tracking
"complianceType": "electrical-inspection",
"inspectionDate": 1740234567,
"nextInspectionDue": 1771770567,
"inspector": "ABC Electrical Services",
"status": "compliant",
// Physical location
"location": {
"building": "Dock 1",
"floor": null,
"room": null,
"gps": {
"lat": 32.7157,
"lon": -117.1611
}
}
}
```
**Property Manager:** "And search needs geo-filtering:"
```javascript
// Search: "fire extinguisher inspection reports near slip B-23"
{
"q": "fire extinguisher inspection",
"filter": "propertyId = 'marina-b' AND location.building = 'Dock B'",
"sort": ["location.gps:asc(32.7157,-117.1611)"] // Nearest first
}
```
---
### CONSENSUS: Property/Marina Schema
```javascript
// Extended for property management
{
// ... base fields ...
// Multi-entity hierarchy
"organizationId": "xyz-corp",
"propertyId": "marina-a",
"facilityId": "dock-1",
"unitId": "slip-42", // For condo units or boat slips
// Property-specific
"propertyType": "marina" | "waterfront-condo" | "yacht-club" | "boat-storage",
"facilityType": "dock" | "building" | "parking" | "storage",
// Compliance
"complianceType": "electrical-inspection" | "fire-safety" | "ada-compliance",
"inspectionDate": 1740234567,
"nextInspectionDue": 1771770567,
"inspectorName": "ABC Services",
"certificateNumber": "CERT-2024-12345",
"status": "compliant" | "pending" | "failed",
// Location
"location": {
"building": "Dock 1",
"floor": 2,
"room": "Electrical Room",
"gps": { "lat": 32.7157, "lon": -117.1611 }
},
// Access control
"visibility": ["marina-a", "org-admin"], // Who can see this doc
// Asset tracking
"assetTag": "ELEC-PANEL-D1-03",
"purchaseDate": 1640234567,
"warrantyExpires": 1771770567,
"vendor": "Siemens"
}
```
---
## PANEL 4: CROSS-VERTICAL PATTERN ANALYSIS
**Duration:** 22 minutes
**Attendees:** All previous panelists
---
### The Pattern Emerges
**Database Architect:** "Looking at all three verticals - boat owners, marinas, and property management - there's a clear pattern:"
```
HIERARCHY PATTERN:
- Organization (company, HOA, family)
└── Properties (boats, marinas, condos)
└── Facilities (systems, docks, units)
└── Components (engine, electrical panel, appliances)
└── Documents (manuals, service records, inspections)
```
**Search Engineer:** "And every vertical needs the SAME core features:"
- Full-text search with synonyms
- Filterable by hierarchy
- Sortable by date/priority
- Offline capability
- Multi-tenant permissions
- Compliance tracking
**Backend Lead:** "So we build ONE platform with vertical-specific views:"
```javascript
// Core schema (all verticals)
{
"id": "doc_unique_id",
// Hierarchy (flexible for all verticals)
"organizationId": "org_123",
"entityId": "entity_456", // boat, marina, condo
"subEntityId": "sub_789", // system, dock, unit
"componentId": "comp_012", // engine, panel, appliance
// Document metadata
"documentType": "manual" | "service-record" | "inspection" | "certificate",
"title": "...",
"text": "...",
// Classification
"categories": ["maintenance", "safety", "compliance"],
"systems": ["electrical", "plumbing", "hvac"],
"tags": ["bilge", "pump", "troubleshooting"],
// Vertical-specific (optional fields)
"vertical": "boating" | "marina" | "property",
"boatingMetadata": { /* boat-specific */ },
"marinaMetadata": { /* marina-specific */ },
"propertyMetadata": { /* property-specific */ },
// Access control
"visibility": ["user_123", "org_admin"],
"permissions": {
"read": ["user_123", "user_456"],
"write": ["user_123"],
"share": ["user_123"]
},
// Compliance (universal)
"complianceType": "...",
"inspectionDate": 1740234567,
"nextDue": 1771770567,
"status": "compliant",
// Timestamps
"createdAt": 1740234567,
"updatedAt": 1740234567
}
```
**Marina Operations Manager:** "Wait, this is genius. I could manage my marina docs AND my personal boat in the same app?"
**All:** "Yes."
---
### FINAL UNIFIED SCHEMA (All Verticals)
**Database Architect:** "SQLite schema - final version:"
```sql
-- Organizations (companies, families, HOAs)
CREATE TABLE organizations (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
type TEXT, -- personal, commercial, hoa
created_at INTEGER NOT NULL
);
-- Entities (boats, marinas, condos)
CREATE TABLE entities (
id TEXT PRIMARY KEY,
organization_id TEXT NOT NULL,
name TEXT NOT NULL,
type TEXT NOT NULL, -- boat, marina, condo, storage-facility
metadata TEXT, -- JSON: boat specs, marina details, condo info
created_at INTEGER NOT NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id)
);
-- Sub-entities (systems, docks, units)
CREATE TABLE sub_entities (
id TEXT PRIMARY KEY,
entity_id TEXT NOT NULL,
name TEXT NOT NULL,
type TEXT, -- system, dock, unit, facility
metadata TEXT, -- JSON
created_at INTEGER NOT NULL,
FOREIGN KEY (entity_id) REFERENCES entities(id)
);
-- Components (engines, panels, appliances)
CREATE TABLE components (
id TEXT PRIMARY KEY,
sub_entity_id TEXT,
name TEXT NOT NULL,
manufacturer TEXT,
model_number TEXT,
serial_number TEXT,
metadata TEXT, -- JSON
created_at INTEGER NOT NULL,
FOREIGN KEY (sub_entity_id) REFERENCES sub_entities(id)
);
-- Documents (universal)
CREATE TABLE documents (
id TEXT PRIMARY KEY,
organization_id TEXT NOT NULL,
entity_id TEXT,
sub_entity_id TEXT,
component_id TEXT,
title TEXT NOT NULL,
document_type TEXT NOT NULL,
file_path TEXT NOT NULL,
file_hash TEXT NOT NULL,
status TEXT DEFAULT 'active',
metadata TEXT, -- JSON
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (organization_id) REFERENCES organizations(id),
FOREIGN KEY (entity_id) REFERENCES entities(id),
FOREIGN KEY (sub_entity_id) REFERENCES sub_entities(id),
FOREIGN KEY (component_id) REFERENCES components(id)
);
-- Users and permissions
CREATE TABLE users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT,
created_at INTEGER NOT NULL
);
CREATE TABLE user_organizations (
user_id TEXT NOT NULL,
organization_id TEXT NOT NULL,
role TEXT DEFAULT 'member', -- admin, manager, member, viewer
PRIMARY KEY (user_id, organization_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (organization_id) REFERENCES organizations(id)
);
CREATE TABLE permissions (
id TEXT PRIMARY KEY,
resource_type TEXT NOT NULL, -- document, entity, organization
resource_id TEXT NOT NULL,
user_id TEXT NOT NULL,
permission TEXT NOT NULL, -- read, write, share, delete
granted_at INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
```
**Search Engineer:** "Meilisearch schema - final version:"
```javascript
// Index: documents-unified
{
// Core identity
"id": "doc_unique_id",
"vertical": "boating" | "marina" | "property",
// Hierarchy
"organizationId": "org_123",
"organizationName": "XYZ Corp",
"entityId": "entity_456",
"entityName": "Sea Breeze / Marina A / Condo Building 3",
"entityType": "boat" | "marina" | "condo",
"subEntityId": "sub_789",
"subEntityName": "Engine / Dock 1 / Unit 305",
"componentId": "comp_012",
"componentName": "Volvo D4 / Electrical Panel 3 / Water Heater",
// Document details
"documentType": "manual" | "service-record" | "inspection" | "certificate",
"title": "...",
"pageNumber": 7,
"text": "Full OCR text...",
// Classification (arrays)
"systems": ["electrical", "navigation"],
"categories": ["maintenance", "safety"],
"tags": ["bilge", "pump", "troubleshooting"],
// Vertical-specific metadata (denormalized for filters)
// BOATING
"boatMake": "Prestige",
"boatModel": "F4.9",
"boatYear": 2024,
"vesselType": "powerboat",
// MARINA
"facilityType": "dock",
"slipNumber": "A-42",
// PROPERTY
"buildingName": "Building A",
"unitNumber": "305",
// Component details
"manufacturer": "Volvo Penta",
"modelNumber": "D4-300",
"serialNumber": "VP-2024-12345",
// Compliance
"complianceType": "electrical-inspection",
"inspectionDate": 1740234567,
"nextDue": 1771770567,
"status": "compliant",
// Access control (filterable)
"userId": "user_123", // Owner/creator
"visibilityUsers": ["user_123", "user_456"], // Who can see this
"organizationVisibility": true, // All org members can see
// Priority
"priority": "critical" | "normal" | "reference",
"offlineCache": true,
// Location (for marinas/properties)
"location": {
"building": "Dock 1",
"gps": { "lat": 32.7157, "lon": -117.1611 }
},
// Timestamps
"createdAt": 1740234567,
"updatedAt": 1740234567,
// Future: embeddings
"embedding": null
}
// Meilisearch Settings
{
"searchableAttributes": [
"title",
"text",
"systems",
"categories",
"tags",
"entityName",
"componentName",
"manufacturer",
"modelNumber"
],
"filterableAttributes": [
"vertical",
"organizationId",
"entityId",
"entityType",
"userId",
"visibilityUsers",
"documentType",
"systems",
"categories",
"boatMake",
"boatModel",
"boatYear",
"vesselType",
"status",
"priority",
"complianceType"
],
"sortableAttributes": [
"createdAt",
"updatedAt",
"pageNumber",
"inspectionDate",
"nextDue"
],
"synonyms": {
// Boating terms
"bilge": ["sump", "drain", "bilge pump"],
"head": ["toilet", "marine toilet", "WC"],
// More...
},
"rankingRules": [
"words",
"typo",
"proximity",
"attribute",
"sort",
"exactness"
]
}
```
---
## FINAL RECOMMENDATIONS
**All Panelists Agree:**
1. **Start with Boating Vertical** - Simpler, fewer compliance requirements
2. **Design for Multi-Vertical** - Schema supports expansion to marinas/properties later
3. **Offline-First** - PWA with service worker, cache critical manuals
4. **Search-First Architecture** - Meilisearch is primary query interface, SQLite is durable storage
5. **Permissions from Day 1** - User-level access control, org-level coming in v1.1
6. **Synonyms Matter** - Boat terminology is non-obvious, configure synonyms upfront
7. **Component Library** - Shared manuals for common components (Volvo engines, Webasto heaters)
8. **Migration Path** - Schema supports SQLite → Postgres, keyword → semantic search, single-tenant → multi-tenant
**Success Metrics:**
- Search latency < 100ms
- Offline mode works without internet
- Synonyms return relevant results ("bilge" finds "sump pump")
- Multi-boat support (one user, multiple boats)
- Manual upload searchable in < 5 minutes
---
**Next:** Extract lilian1 clean code, build NaviDocs with this schema.