| name | menu-verification |
| description | Use this skill to verify whether restaurants actually have Planted products on their current menu. Crawls restaurant websites, parses PDF menus, and updates venue verification status in Firestore. |
Menu Verification Skill
This skill verifies whether discovered venues actually have Planted products on their current menus. It's essential for maintaining data quality - press releases and discovery don't guarantee ongoing availability.
When This Skill Activates
Triggers on:
- Verifying newly discovered venues
- Re-checking venues marked as
unverified - Validating venues from press releases
- Auditing existing venue data quality
Quick Start
Run the Verification Agent
cd planted-availability-db/packages/scrapers
# Verify all unverified venues with websites
node verify-menu-agent.cjs
# Verify a specific venue by ID
node verify-menu-agent.cjs --venue-id=VENUE_ID_HERE
What the Agent Does
- Finds unverified venues with websites in Firestore
- Crawls the website including subpages (/restaurant, /en, /de, etc.)
- Discovers PDF links on the pages
- Tries common PDF paths directly (/media/menu.pdf, /speisekarte.pdf, etc.)
- Parses PDF content using pdf-parse library
- Searches for Planted keywords (planted, plant-based, geschnetzeltes, etc.)
- Updates Firestore with verification status and any dishes found
Verification Statuses
| Status | Meaning |
|---|---|
verified_on_menu |
Planted products confirmed on current menu |
not_found_on_menu |
No Planted products found after thorough check |
unverified |
Not yet checked |
needs_manual_check |
Website couldn't be crawled, needs human verification |
Manual Verification Workflow
For venues the agent can't verify automatically:
1. Check the Website Directly
# Get venue details
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
db.collection('venues').doc('VENUE_ID').get().then(d => console.log(JSON.stringify(d.data(), null, 2)));
"
2. Look for PDF Menus
Common PDF locations to check:
/media/menu.pdf,/media/speisekarte.pdf/downloads/menu.pdf/restaurant/menu.pdf- Language variants:
_en.pdf,_de.pdf,_d.pdf
3. Search for Planted Keywords
In PDFs or HTML, search for:
planted(brand name)plant-based,plant based,pflanzenbasiertgeschnetzeltes(common planted dish)planted chicken,planted kebab,planted steak
4. Update the Venue
# Mark as verified with planted products
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
db.collection('venues').doc('VENUE_ID').update({
status: 'active',
verification_status: 'verified_on_menu',
verification_date: new Date(),
verification_method: 'manual',
verification_note: 'Found X planted dishes on menu PDF',
updated_at: new Date()
});
"
# Or mark as not found
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
db.collection('venues').doc('VENUE_ID').update({
status: 'unverified',
verification_status: 'not_found_on_menu',
verification_date: new Date(),
verification_method: 'manual',
verification_note: 'No planted products found on menu',
updated_at: new Date()
});
"
Adding Dishes from Verified Menus
When planted products are found:
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
const dish = {
venue_id: 'VENUE_ID',
name: 'Planted Geschnetzeltes mit Rösti',
description: 'Swiss-style planted strips with hash browns',
price: { amount: 32.00, currency: 'CHF' },
planted_products: ['planted.geschnetzeltes'],
status: 'active',
source: 'menu_verification',
created_at: new Date(),
updated_at: new Date()
};
db.collection('dishes').add(dish).then(ref => console.log('Added dish:', ref.id));
"
Planted Product SKUs
Common product identifiers:
planted.chicken- Planted Chickenplanted.geschnetzeltes- Planted Geschnetzeltes (strips)planted.kebab- Planted Kebabplanted.steak- Planted Steakplanted.pulled- Planted Pulledplanted.schnitzel- Planted Schnitzel
Removing Invalid Venues
If a venue doesn't have planted products and was added in error:
# Delete venue (and its dishes)
node -e "
const admin = require('firebase-admin');
admin.initializeApp({ credential: admin.credential.cert('../../service-account.json') });
const db = admin.firestore();
const venueId = 'VENUE_ID';
// Delete dishes first
db.collection('dishes').where('venue_id', '==', venueId).get()
.then(snap => {
const batch = db.batch();
snap.docs.forEach(doc => batch.delete(doc.ref));
return batch.commit();
})
.then(() => db.collection('venues').doc(venueId).delete())
.then(() => console.log('Deleted venue and dishes'));
"
Agent Configuration
The verify-menu-agent.cjs searches for these patterns:
Subpages Crawled
const SUBPAGE_PATTERNS = [
'/restaurant', '/en', '/de', '/bar', '/cuisine', '/kulinarik'
];
PDF Paths Tried
const COMMON_PDF_PATHS = [
'/media/menu.pdf', '/media/speisekarte.pdf',
'/media/restaurant_mo_-_fr_d.pdf', // Kronenhalle pattern
'/downloads/menu.pdf', '/menu.pdf'
];
Keywords Searched
const PLANTED_KEYWORDS = [
'planted', 'plant-based', 'plant based', 'pflanzenbasiert',
'planted chicken', 'planted kebab', 'planted geschnetzeltes'
];
Dependencies
The agent requires:
npm install pdf-parse # For PDF text extraction
Example Session
$ node verify-menu-agent.cjs
=== Menu Verification Agent ===
Found 3 unverified venues with websites
Checking: Kronenhalle (https://www.kronenhalle.com)
Crawling homepage...
Found 2 PDF links
Parsing: restaurant_mo_-_fr_d.pdf (12 pages)
FOUND: "Planted Geschnetzeltes «Kronenhalle» mit Rösti"
FOUND: "Planted Steak mit Sanddorn"
Status: verified_on_menu (2 dishes added)
Checking: Lindenhofkeller (https://www.lindenhofkeller.ch)
Crawling homepage + subpages...
Found 3 PDF links
Parsing: drinks.pdf (31 pages) - no planted
Parsing: lunch.pdf (4 pages) - no planted
Parsing: dinner.pdf (6 pages) - no planted
Status: not_found_on_menu
=== Summary ===
Verified: 1
Not found: 1
Errors: 0
Troubleshooting
PDF Not Parsing
- Check if pdf-parse is installed:
npm ls pdf-parse - Some PDFs are image-based (scanned) - need manual check
- Compressed PDFs may fail - try downloading manually
Website Blocking
- Some sites block automated crawlers
- Try with different User-Agent
- May need manual verification
No Menu Found
- Check if restaurant uses third-party menu systems
- Look for iframe embeds
- Check delivery platform listings instead