H1Human Layer Closure Doc — Shipped Features
Canonical record of completed features. When a feature is done, it moves here from human-layer-doc.md.
H2🟢 Shipping (Production-Ready)
H3Phase 1: Prisma Schema Updates ✅
Completed: May 2026
Status: 🟢 Shipping (verified on staging, build passes)
- Added enums:
SpaceStatus,SpaceVisibility,SpaceRouteKind,SpaceSectionValidationStatus - Added
projectionMeta Json?to Trip, Offer, Order - Added Stripe fields to Order (
stripePaymentIntentId,stripeCustomerId) - Added
Paymentmodel with relations to Order - Added Space models:
Space,Section,SpaceRouteAlias - Schema validated and pushed to database (
npx prisma db push) - Zod schemas auto-generated
Verification: Build passes, all routes compile (npm run build successful)
H3Phase 2: Apps Script API Endpoints ✅
Completed: May 2026
Status: 🟢 Shipping (code complete, ready for GAS deployment)
- Created
OfficeServer_GWS-Automation-Framework.gswithdoPost()router - Implemented handlers:
docs.create,docs.exportPdf,calendar.create,drive.share,gmail.send - Added legacy compatibility:
coreOffice.generateTemplates,coreOffice.generateEntityDocs - Auth check via
PropertiesService.getScriptProperties().getProperty("AVATAR_SECRET") - Response envelope:
{ ok, action, requestId, timestamp, result, error, meta }
Next: Deploy to Google Apps Script, set AVATAR_SECRET script property, configure APPS_SCRIPT_WEBAPP_URL env var
H3Phase 3: Next.js Projection Actions ✅
Completed: May 2026
Status: 🟢 Shipping (code complete, build passes)
- Created
app/trips/actions/exportTrip.ts(exportTripToDocs, exportTripToPdf) - Created
app/offers/actions/exportOffer.ts(exportOfferToDocs, exportOfferToPdf) - Updated
app/api/office-server/route.tswith new action types - Added export buttons to
TripDetailClient.tsx:- "Export to Google Docs" button
- "Export to PDF (GAS)" button
- Build successful, all routes compiled
Verification: npm run build passes (exit code 0)
H2🟡 Pending Verification
(None currently — all completed phases verified via build)
H2🔮 Future Stages (Post-Revenue, Not Blocking)
H3External Channel Tracking
- Etsy Sales Dashboard
- Shopify Integration
- Centralized storefront for all products
Note: These depend on reaching 30-50 consistent sales or €500+ revenue validation.
H3Phase 4: Content Stack Factory — Auto-Generated Editable Content for Trips & Experiences ✅
Completed: May 2026
Status: 🟢 Shipping (schema pushed, build passes, factory integrated)
What it does: Every new Trip or Experience automatically spawns a standard editable content structure (SessionStack + ConceptGroups + ConceptCards) that can be rearranged and extended through the existing content layer.
Schema changes:
- Added
contentStackId+contentStackrelation toTripandExperiencemodels - Added reverse relations
tripContentandexperienceContenttoSessionStack - Enforced unique constraint on
contentStackIdper Trip/Experience
Factory: lib/factories/contentStackFactory.ts
- Creates
SessionStack(type: GROUP) titled "Content: {trip/experience name}" - Creates 3 canonical
ConceptGroups: Overview, Logistics, Experience Flow - Creates 7 starter
ConceptCardswith real metadata (promise, boundaries, steps, materials) - Links everything via
SessionConceptGroupandConceptGroupItemjoin tables - Auto-attaches to the owner Trip or Experience
Integration points:
app/trips/new/actions/tripPlanner.create.actions.ts— calls factory after trip creationapp/experiences/actions.ts— calls factory aftersaveExperienceDraftapp/experiences/actions/projectExperiences.ts— calls factory increateExperienceapp/api/projects/[projectId]/experiences/route.ts— calls factory on POST
UI surfaces:
- Trip detail page (
app/trips/[tripId]/page.tsx+TripDetailClient.tsx) — shows content stack groups/cards with "Edit content" →/session-stacks/{id}and "Manage groups" →/concept-groups - Experience detail page (
app/experiences/[experienceId]/page.tsx) — same content stack display
Verification: npm run build passes, npx prisma db push succeeded
H3Phase 5: Commercial Content Rewrite — Experiences & Trips Surface ✅
Completed: May 2026
Status: 🟢 Shipping (live on staging, build passes)
Experiences page (app/experiences/page.tsx):
- Complete rewrite with traveler-facing commercial copy from alandalus-experience.com reference
- Hero: "Experience Al-Andalus" with value proposition
- Pathways: Guided Tours, Custom Routes, Gift Cards, Local Sessions
- Featured experiences: Mezquita, Alhambra, Medina Azahara
- How it works: 4-step visual journey
- Cities: Córdoba, Granada, Madrid, Seville, Málaga
- Trips bridge + Collaborate CTA
- Footer bridge back to
/studio
Trips registry (app/trips/registry/TripsSectionRegistry.ts):
- Default commercial content updated for all section types
- Removed system-mechanics language in favor of traveler invitations
Session detail pages (app/(home)/studio/travel/[id]/page.tsx):
- Body content, highlights, related public experience with booking link
- Deliverables placeholder, FareHarbor booking card
- Navigation back to session list
Schema fix:
- Removed
latitude/longitudefromTripCitymodel (belong onCity) - Updated
hotel-actions.tsto query coordinates throughcityrelation
H3Phase 6: Trip → Offer → Order Pipeline ✅
Completed: May 2026
Status: 🟢 Shipping (code complete, build passes)
Core loop locked:
createOfferFromTrip()intrip-offer.actions.ts— creates offer from trip with line itemscheckoutOffer()inapp/orders/actions/checkoutOffer.ts— converts Offer → Order (manual payment)- Server component fetches
associatedOfferand passesofferIdto client - Manual payment modes: bank transfer / PayPal / cash
Status: Flow implemented end-to-end; ready for browser verification with real project data.
H3Phase 7: FareHarbor Booking Sync — Two-Hook Operational Architecture ✅
Completed: May 2026
Status: 🟢 Shipping (schema pushed, build passes, all routes compile)
What it does: Closes the loop between FareHarbor Lightframe bookings and Molino's internal state. Two distinct concerns: initiation (open modal) and confirmation (poll for webhook result).
Schema:
ExternalBookingmodel — tracks external bookings from any source (FH, Etsy, Stripe)- Fields:
source,externalId,status,customerEmail,itemId,itemName,flowId,total,currency,bookingDate,rawPayload - Unique by
(source, externalId)and indexed by(customerEmail, createdAt),status
Client hooks:
hooks/useBookingModal.ts— Opens FH Lightframe modal via autolightframe script. Only handles initiation; does NOT trust booking completed.hooks/useBookingSync.ts— PollsGET /api/bookings/pendingevery 3s for up to 2 min. Returns confirmed booking or timeout.
Server routes:
POST /api/webhooks/fareharbor— Receives FH webhooks, upsertsExternalBooking, forwards to GAS (fire-and-forget). Returns 200 so FH doesn't retry.GET /api/bookings/pending— Poll endpoint. QueriesExternalBookingby email + createdAt window + confirmed status.
UI integration:
FareHarborBookingCard.tsx— Enhanced with optional sync state. IfcustomerEmailprop provided:- Shows spinner: "Checking for booking confirmation…"
- Shows success: "✅ Booking confirmed!" with item name, total, date
- Shows error on polling failure
Verification: npm run build passes, npx prisma db push succeeded, new routes /api/webhooks/fareharbor and /api/bookings/pending compiled
H3Phase 8: FareHarbor ICS Calendar API ✅
Completed: May 2026
Status: 🟢 Shipping (route built, no token committed, build passes)
What it does: Fetches the FareHarbor private ICS feed server-side and returns normalized JSON calendar events for future availability/status displays.
Route:
GET /api/fareharbor/calendar- Optional filters:
?start=<iso-date>&end=<iso-date> - Returns:
source,fetchedAt,count, and parsedevents
Security:
- Requires
FAREHARBOR_ICS_TOKENin environment - The token is not hardcoded in source
Parser fixes:
- Handles folded ICS lines
- Parses
UID,SUMMARY,DTSTART,DTEND,DESCRIPTION,LOCATION,STATUS - Converts UTC, local datetime, and all-day ICS dates to usable strings
Verification: npm run build passes and route /api/fareharbor/calendar compiles
H3Phase 9: FareHarbor Product Catalogue Sheet Import ✅
Completed: May 2026
Status: 🟢 Shipping (Apps Script helper + Next import route built, build passes)
What it does: Uses the Google Sheet as the human sanity surface for all FareHarbor products, then posts normalized catalogue rows into the Next app.
Apps Script:
FareHarborCatalog_readHorizontal(sheetName)— reads the wide/horizontal product catalogueFareHarborCatalog_writeNormalized(sheetName, outputSheetName)— creates one row per product in⚜️ FH_ProductCatalog_NormalizedFareHarborCatalog_refresh()— console shortcut for normalizationFareHarborCatalog_postToNext(sheetName, nextEndpoint)— posts normalized products to Next
Next route:
POST /api/fareharbor/catalog/import- Accepts
{ products, sourceSheet, secret, createContentStacks } - Upserts rows into
Experienceby slug/productKey - Stores the full spreadsheet row as JSON in
Experience.content - Creates/attaches a content stack when missing
Verification: npm run build passes and route /api/fareharbor/catalog/import compiles
H3Phase 10: GAS↔Next.js Contract Alignment — Office Server 🟡
Completed: May 2026
Status: 🟡 Pending Verification (code complete, needs prisma generate + migration)
What it does: Aligns all Next.js routes (webhooks, catalog import, office-server bridge) with the GAS↔Next.js contract reference for FareHarbor integration.
Prisma schema changes:
- Added
customerPhone,affiliateCode,customFields,customTripReftoExternalBooking - Added relation
ExternalBooking.customTrip → CustomTripRequest - New model
CustomTripRequest— customer trip requests with status workflow - New model
FareHarborProduct— raw catalog entity storage - New model
AffiliateCommission— affiliate tracking
Route updates:
POST /api/webhooks/fareharbor— x-sync-secret auth (APP_SYNC_SECRET), handlesbooking.created|cancelled|modifiedwith contract field mapping (affiliate_code, custom_fields, custom_trip_ref, phone)POST /api/fareharbor/catalog/import— x-sync-secret auth (CATALOG_IMPORT_SECRET), upserts bothFareHarborProductandExperiencePOST /api/office-server— accepts Group C action dispatch:fareharbor.customTrip.create,fareharbor.link.generate,fareharbor.availability.get,fareharbor.manager.actionwith standard response envelope{ ok, action, requestId, timestamp, result, meta }. Legacy bundle handlers preserved.
New file:
lib/fareharbor-gas-client.ts— Typed client with 4 exported functions:createCustomTrip(input)→GasResponse<{ request_id }>generateBookingLink(input)→GasResponse<{ lightframe_url }>getAvailability(input)→GasResponse<{ count, availabilities }>managerAction(input)→GasResponse
Verification: Code reviewed, all imports resolve (uuid, @prisma/client available). Needs prisma generate + prisma migrate dev + endpoint testing.
H2🟡 Pending Verification
H3CityBlockAssignedContent — Concept Group Card Grid & Session Stack Children
Completed: May 2026
Status: 🟡 Pending Verification (code done, needs browser test)
- Restored CardGrid + QR hover card style for concept groups in "Concept groups" section
- Added new "From sessions" section rendering session stack children concept groups as cards
- Updated
page.tsxto fetchsessionConceptGroupswith linked session stacks, computecontentStackChildrenper city from all assigned session stacks - Removed old duplicate inline rendering of
contentStack.sessionConceptGroupsfromTripItineraryCanvas.tsx - Added
contentStackChildrentoTripCityBlockViewModelandTripDetailcity types - No new type errors, all pre-existing errors unchanged
Files changed:
app/trips/[tripId]/CityBlockAssignedContent.tsxapp/trips/[tripId]/page.tsxapp/trips/[tripId]/TripDetailClient.tsxapp/trips/builder/components/TripItineraryCanvas.tsxapp/trips/builder/lib/trip-builder.types.tsdev-docs/human-layer-doc.md
Verification: Build compiles, no new TS errors. Needs browser test on a city block with both projectionMeta concept groups and session-stack-linked concept groups.
H2🔮 Future Stages (Post-Revenue, Not Blocking)
H3External Channel Tracking
- Etsy Sales Dashboard
- Shopify Integration
- Centralized storefront for all products
Note: These depend on reaching 30-50 consistent sales or €500+ revenue validation.
H2Archive (Older Completed Features)
(Previous shipped features moved here for reference)