EPGOAT Quick Start Guide
Status: Active
Last Updated: 2025-11-02
Related Docs: System Overview, Command Reference, Development Setup
Code Location: backend/epgoat/, frontend/
Target Audience: New developers joining the EPGOAT project Time to Complete: 30-45 minutes Prerequisites: macOS/Linux, Git, Python 3.11+, Node.js 18+, GitHub account
Table of Contents
- Overview
- Development Environment Setup
- Running EPG Generation Locally
- Running the Admin Frontend
- Testing
- Common Workflows
- Troubleshooting
- Next Steps
Overview
What is EPGOAT?
EPGOAT is a SaaS platform that generates Electronic Program Guides (EPG) for IPTV services. It:
- Parses M3U playlists from IPTV providers
- Matches channels to sports events using a 6-stage regex matcher + LLM fallback
- Generates XMLTV files for EPG consumption (Plex, Jellyfin, etc.)
- Provides multi-tenant access via UUID-based keys
- Manages subscriptions via Stripe with Auth0 authentication
Architecture at a Glance:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β EPGOAT PLATFORM β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Marketing β β Consumer β β Admin β β
β β Website β β Portal β β Dashboard β β
β β epgoat.tv β β app.epgoat.tvβ βadmin.epgoat.tvβ β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β β β β
β ββββββββββββββββββββββ΄βββββββββββββββββββββ β
β β β
β βββββββββββΌββββββββββ β
β β Cloudflare β β
β β Workers API β β
β β (Serverless) β β
β βββββββββββ¬ββββββββββ β
β β β
β βββββββββββββββββΌββββββββββββββββ β
β β β β β
β ββββββββΌββββββ βββββββΌβββββββ βββββββΌβββββββ β
β β Cloudflare β β Cloudflare β β Stripe β β
β β D1 β β R2 β β (Billing) β β
β β (Database) β β (Storage) β ββββββββββββββ β
β ββββββββββββββ ββββββββββββββ β
β β β
β βββββββββββΌββββββββββ β
β β GitHub Actions β β
β β EPG Generation β β
β β (4x daily cron) β β
β βββββββββββββββββββββ β
β β β
β βββββββββββΌββββββββββ β
β β TheSportsDB API β β
β β (Event Data) β β
β βββββββββββββββββββββ β
β β β
β βββββββββββΌββββββββββ β
β β Claude API β β
β β (LLM Matching) β β
β βββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Repository Structure:
epgoat-internal/
βββ engineering/
β βββ linker/ # Python EPG generation engine
β β βββ core/ # Parsers, patterns, models
β β βββ backend/epgoat/services/ # API clients, LLM matcher
β β βββ data/ # Enhanced matchers
β β βββ pipeline/ # Main EPG generator
β β βββ tests/ # Unit tests
β βββ config/ # YAML configuration files
β βββ scripts/ # Utility scripts
βββ frontend/
β βββ admin/ # Admin dashboard (React + MUI)
β βββ consumer/ # Consumer portal (React + MUI)
β βββ marketing/ # Marketing website (React)
βββ Documentation/ # Documentation
βββ schema.sql # Database schema (27 tables)
βββ API_SPECIFICATION.md # OpenAPI spec
βββ TECH_STACK.md # Technology decisions
βββ README.md # Project overview
Development Environment Setup
Step 1: Clone the Repository
# Clone the repository
git clone https://github.com/epgoat/epgoat-internal.git
cd epgoat-internal
# Checkout main branch
git checkout main
Step 2: Python Environment Setup
# Create virtual environment
python3 -m venv venv
# Activate virtual environment
source venv/bin/activate # macOS/Linux
# OR
venv\Scripts\activate # Windows
# Upgrade pip
pip install --upgrade pip
# Install dependencies
pip install -r requirements.txt
# Verify installation
python --version # Should be 3.11+
pytest --version # Should be installed
ruff --version # Should be installed
Requirements Overview:
pydantic==2.5.0 # Data validation
httpx==0.25.2 # Async HTTP client
lxml==5.0.0 # XML parsing/generation
anthropic==0.18.0 # Claude API client
python-dotenv==1.0.0 # Environment variables
pyyaml==6.0.1 # YAML config loading
pytest==7.4.3 # Testing
ruff==0.1.9 # Linting/formatting
Step 3: Environment Variables
# Copy example environment file
cp .env.example .env
# Edit .env with your credentials
nano .env # or use your favorite editor
Required Environment Variables:
# .env
# TheSportsDB API (required for event data)
THESPORTSDB_API_KEY=your_api_key_here
# Get free key at: https://www.thesportsdb.com/api.php
# Anthropic Claude API (required for LLM matching)
ANTHROPIC_API_KEY=your_api_key_here
# Get key at: https://console.anthropic.com/
# Optional: Provider M3U URLs with credentials
TPS_M3U_URL=http://username:password@provider.com/playlist.m3u
Getting API Keys:
- TheSportsDB API Key:
- Visit https://www.thesportsdb.com/api.php
- Sign up for Patreon ($2/month) to get API key
-
Or use free test key (limited to 100 requests/day)
-
Anthropic Claude API Key:
- Visit https://console.anthropic.com/
- Create account
- Add $5 credit to start
- Copy API key from Settings
Step 4: Node.js Environment Setup (for Frontend)
# Navigate to admin frontend
cd frontend/admin
# Install dependencies
npm install
# Verify installation
npm --version # Should be 18+
node --version # Should be 18+
Step 5: Verify Installation
# Run Python tests
cd backend/epgoat
pytest -v
# Expected output:
# ==================== test session starts ====================
# collected 45 items
#
# test_patterns.py::test_nba_pattern PASSED
# test_patterns.py::test_nfl_pattern PASSED
# ...
# ==================== 45 passed in 2.34s ====================
# Run linting
ruff check .
# Expected output:
# All checks passed!
# Run type checking
mypy .
# Expected output:
# Success: no issues found in 23 source files
If tests fail:
- Ensure you're in the correct directory (backend/epgoat)
- Verify Python version is 3.11+
- Check that all dependencies are installed: pip list
- Review error messages for missing environment variables
Running EPG Generation Locally
Basic EPG Generation
Scenario: Generate an EPG for today for a single provider.
# Navigate to EPG pipeline directory
cd backend/epgoat/pipeline
# Run EPG generator
python epg_generator.py \
--provider tps \
--date today \
--timezone "America/New_York"
# Expected output:
# [INFO] Loading configuration...
# [INFO] Fetching M3U playlist for provider: tps
# [INFO] Parsed 4,876 channels from M3U
# [INFO] Stage 1 (League prefix): 1,234 matches (25.3%)
# [INFO] Stage 2 (Team names): 892 matches (18.3%)
# [INFO] Stage 3 (Event time): 456 matches (9.4%)
# [INFO] Stage 4 (Abbreviations): 234 matches (4.8%)
# [INFO] Stage 5 (Fuzzy matching): 178 matches (3.6%)
# [INFO] Stage 6 (LLM fallback): 1,123 matches (23.0%)
# [INFO] Total matched: 4,117 / 4,876 (84.4%)
# [INFO] Unmatched: 759 channels (15.6%)
# [INFO] Generating XMLTV file...
# [INFO] Written to: output/tps_2025-10-30.xml
# [INFO] LLM cost: $0.42 (1,123 calls, 90% cached)
Output Files:
backend/epgoat/output/
βββ tps_2025-10-30.xml # Generated XMLTV file
βββ unmatched_channels.json # Channels that failed to match
βββ match_report.json # Detailed match statistics
Generate EPG for Specific Date
# Generate EPG for specific date (e.g., upcoming weekend)
python epg_generator.py \
--provider tps \
--date 2025-11-15 \
--timezone "America/Chicago"
# Generate EPG for date range (future feature)
python epg_generator.py \
--provider tps \
--start-date 2025-11-15 \
--end-date 2025-11-22 \
--timezone "America/Los_Angeles"
Test with Sample M3U File
# Use a local test M3U file (no API calls)
python epg_generator.py \
--m3u-file ../tests/fixtures/sample_playlist.m3u \
--date today \
--timezone "America/New_York" \
--skip-api # Don't call TheSportsDB API (use cached events)
# This is useful for:
# - Testing pattern changes without API costs
# - Working offline
# - Reproducible test runs
Debug Mode
# Enable verbose logging to see detailed matching process
python epg_generator.py \
--provider tps \
--date today \
--debug
# Expected output (verbose):
# [DEBUG] Channel: "NBA 01: Lakers vs Celtics 7:00 PM ET"
# [DEBUG] Stage 1: Testing pattern: ^NBA\s+\d+
# [DEBUG] Stage 1: MATCH! Extracted: league=NBA
# [DEBUG] Searching events for: league=NBA, date=2025-10-30
# [DEBUG] Found 12 NBA events today
# [DEBUG] Testing team names: "Lakers" vs "Los Angeles Lakers" (score: 0.95)
# [DEBUG] Testing team names: "Celtics" vs "Boston Celtics" (score: 0.98)
# [DEBUG] MATCHED to event_id=12345 (confidence: 0.96)
Refresh Event Database
Before generating EPGs, refresh the event database from TheSportsDB:
# Navigate to utilities
cd backend/epgoat/utilities
# Refresh events for today
python refresh_event_db.py --date today
# Expected output:
# [INFO] Fetching events from TheSportsDB for 2025-10-30
# [INFO] Found 147 events across 8 sports
# [INFO] Basketball: 45 events (NBA, EuroLeague, NCAA)
# [INFO] American Football: 32 events (NFL, NCAA)
# [INFO] Soccer: 52 events (Premier League, La Liga, etc.)
# [INFO] Ice Hockey: 12 events (NHL)
# [INFO] Baseball: 0 events (off-season)
# [INFO] MMA: 3 events (UFC, Bellator)
# [INFO] Boxing: 2 events
# [INFO] Motor Racing: 1 event (F1)
# [INFO] Inserted 147 events into database
# [INFO] Updated 23 existing events
# Refresh events for specific date range
python refresh_event_db.py \
--start-date 2025-11-15 \
--end-date 2025-11-22
# Refresh leagues/competitions metadata
python refresh_leagues.py
Cost Note: TheSportsDB API has rate limits (100 requests/day on free tier). Use sparingly.
Running the Admin Frontend
Step 1: Start Development Server
# Navigate to admin frontend
cd frontend/admin
# Start Vite dev server
npm run dev
# Expected output:
#
# VITE v5.0.8 ready in 342 ms
#
# β Local: http://localhost:5173/
# β Network: use --host to expose
# β press h + enter to show help
Step 2: Access Admin Dashboard
Open your browser to: http://localhost:5173/
Default Login (Development): - Email: admin@epgoat.tv - Password: admin123 (development only!)
Pages Available:
- Dashboard (
/) - Match rate metrics
- Daily statistics
-
Recent unmatched channels
-
Providers (
/providers) - View all IPTV providers
- Edit M3U URLs
-
Enable/disable LLM fallback
-
Events (
/events) - Search TheSportsDB events
- View event details
-
Manual channel matching
-
Unmatched Channels (
/unmatched) - View channels that failed to match
- Manually link to events
-
Submit as learned patterns
-
Users (
/users) - View all users
- Manage subscriptions
-
Suspend/reactivate accounts
-
Analytics (
/analytics) - Match rate trends
- LLM cost tracking
- User growth metrics
Step 3: Configure Auth0 (Optional)
For full authentication flow, set up Auth0:
# Edit .env in frontend/admin/
nano .env
# Add Auth0 credentials:
VITE_AUTH0_DOMAIN=epgoat.us.auth0.com
VITE_AUTH0_CLIENT_ID=your_client_id_here
VITE_AUTH0_AUDIENCE=https://api.epgoat.tv
To get Auth0 credentials:
- Sign up at https://auth0.com/ (free tier)
- Create a new Single Page Application
- Set Allowed Callback URLs:
http://localhost:5173 - Set Allowed Logout URLs:
http://localhost:5173 - Copy Domain and Client ID to
.env
Without Auth0: The app will use mock authentication (always logged in as admin).
Step 4: Hot Module Replacement (HMR)
Edit any React component and see changes instantly:
# Edit a component
nano src/pages/Dashboard.tsx
# Save file (Cmd+S / Ctrl+S)
# Browser auto-refreshes with changes (no manual reload needed)
Testing
Python Unit Tests
# Navigate to linker directory
cd backend/epgoat
# Run all tests
pytest
# Run specific test file
pytest tests/test_patterns.py
# Run specific test function
pytest tests/test_patterns.py::test_nba_pattern_matching
# Run with coverage report
pytest --cov=core --cov=services --cov-report=html
# View coverage report
open htmlcov/index.html # macOS
xdg-open htmlcov/index.html # Linux
Test Categories:
- Pattern Tests (
test_patterns.py) - Test regex patterns for all leagues
- Verify stage-by-stage matching
-
Edge cases (special characters, unicode)
-
Parser Tests (
test_parsers.py) - M3U parsing
- Time extraction
-
Channel normalization
-
Integration Tests (
test_integration.py) - End-to-end EPG generation
- API mocking (TheSportsDB, Claude)
- XMLTV validation
Writing New Tests:
# tests/test_patterns.py
import pytest
from core.patterns import ChannelMatcher
def test_custom_league_pattern():
"""Test matching for a new league pattern."""
matcher = ChannelMatcher()
channel_name = "UFC 301: Main Event 10:00 PM ET"
result = matcher.match(channel_name)
assert result is not None
assert result.league == "UFC"
assert result.confidence > 0.9
assert result.stage == 1 # Matched in stage 1 (league prefix)
Frontend Tests
# Navigate to admin frontend
cd frontend/admin
# Run Vitest tests
npm run test
# Run with UI
npm run test:ui
# Run with coverage
npm run test:coverage
Frontend Test Example:
// src/components/SubscriptionCard.test.tsx
import { render, screen } from '@testing-library/react';
import { SubscriptionCard } from './SubscriptionCard';
test('displays active subscription correctly', () => {
const subscription = {
status: 'active',
plan_type: 'consumer_annual',
current_period_end: '2026-10-30',
};
render(<SubscriptionCard subscription={subscription} />);
expect(screen.getByText('Active')).toBeInTheDocument();
expect(screen.getByText('Consumer Annual')).toBeInTheDocument();
});
Linting & Formatting
# Python: Ruff (replaces Flake8 + Black)
cd backend/epgoat
# Check for issues
ruff check .
# Auto-fix issues
ruff check --fix .
# Format code
ruff format .
# TypeScript/React: ESLint + Prettier
cd frontend/admin
# Lint
npm run lint
# Format
npm run format
# Type check
npm run type-check
Common Workflows
Workflow 1: Adding Support for a New League
Scenario: Add support for "LIV Golf" league.
Steps:
- Add emoji configuration:
# Edit config file
nano backend/config/sport_emojis.yml
# Add entry:
Golf: β³
LIV Golf: β³
- Add XMLTV category:
# Edit config file
nano backend/config/sport_categories.yml
# Add entry:
Golf:
- Sports
- Golf
LIV Golf:
- Sports
- Golf
- Professional
- Add channel pattern:
# Edit patterns file
nano backend/epgoat/domain/patterns.py
# Add to ALLOWED_CHANNEL_PATTERNS:
(r'^LIV\s+Golf\s+\d+', 'LIV Golf'), # "LIV Golf 1", "LIV Golf 2"
(r'^LIV\s+\d+', 'LIV Golf'), # "LIV 1", "LIV 2"
- Add test:
# Add to test file
nano backend/epgoat/tests/test_patterns.py
def test_liv_golf_pattern():
"""Test LIV Golf channel matching."""
matcher = ChannelMatcher()
test_cases = [
"LIV Golf 1",
"LIV Golf 2: Singapore Day 1",
"LIV 3: Final Round",
]
for channel in test_cases:
result = matcher.match(channel)
assert result is not None
assert result.league == "LIV Golf"
assert result.confidence > 0.8
- Run tests:
pytest tests/test_patterns.py::test_liv_golf_pattern -v
- Commit changes:
git add -A
git commit -m "feat(patterns): add LIV Golf league support"
git push origin feature/add-liv-golf
Workflow 2: Debugging a Failed Match
Scenario: A channel "NBA Championship: Lakers vs Celtics" is not matching.
Steps:
- Run EPG generator in debug mode:
cd backend/epgoat/pipeline
python epg_generator.py \
--provider tps \
--date today \
--debug \
--channel-filter "NBA Championship"
# Look for debug output showing why it failed
- Check unmatched channels:
# View unmatched channels JSON
cat output/unmatched_channels.json | jq '.[] | select(.channel_name | contains("NBA Championship"))'
# Example output:
# {
# "channel_name": "NBA Championship: Lakers vs Celtics",
# "regex_stage_reached": 0,
# "llm_attempted": false,
# "failure_reason": "No regex pattern matched"
# }
- Test pattern manually:
# Create test script
nano test_pattern.py
from core.patterns import ChannelMatcher
matcher = ChannelMatcher()
channel = "NBA Championship: Lakers vs Celtics"
result = matcher.match(channel)
print(f"Match result: {result}")
print(f"Stage: {result.stage if result else 'None'}")
print(f"Confidence: {result.confidence if result else 0.0}")
# Run test
python test_pattern.py
- Add missing pattern:
# If pattern is missing, add it:
nano backend/epgoat/domain/patterns.py
# Add to ALLOWED_CHANNEL_PATTERNS (in appropriate order):
(r'^NBA\s+Championship', 'NBA'), # Handle "NBA Championship" variant
- Verify fix:
# Re-run generator
python epg_generator.py --provider tps --date today --channel-filter "NBA Championship"
# Should now match successfully
Workflow 3: Manual Channel-to-Event Linking
Scenario: Admin user manually links an unmatched channel to an event.
Steps:
- Open Admin Dashboard:
http://localhost:5173/unmatched
- Find unmatched channel:
- Filter by provider: "TPS"
- Search: "Lakers"
-
Click on channel row
-
Search for event:
- Event search modal opens
- Search: "Lakers Celtics"
-
Select correct event from results
-
Set confidence:
- Confidence slider: 0.95 (very confident match)
-
Click "Link Channel to Event"
-
Verify database update:
-- Check unmatched_channels table
SELECT * FROM unmatched_channels
WHERE channel_name LIKE '%Lakers%'
AND manual_match_event_id IS NOT NULL;
- Pattern learning (automatic):
- System analyzes the manual match
- Suggests new regex pattern
- Admin approves pattern
- Pattern added to
learned_patternstable
Workflow 4: Deploying EPG Generation to Production
Scenario: Deploy EPG generation workflow to GitHub Actions.
Steps:
- Verify workflow file:
cat .github/workflows/epg-generation.yml
- Add secrets to GitHub:
- Go to repository Settings β Secrets and variables β Actions
-
Add secrets:
THESPORTSDB_API_KEYANTHROPIC_API_KEYCLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
-
Test workflow locally with act (optional):
# Install act: https://github.com/nektos/act
brew install act # macOS
# Run workflow locally
act schedule --secret-file .env
- Trigger manual workflow:
- Go to GitHub repository β Actions tab
- Select "EPG Generation" workflow
- Click "Run workflow"
- Select branch:
main -
Click "Run workflow"
-
Monitor execution:
- Watch workflow logs in real-time
- Check for successful R2 upload
-
Verify XMLTV files are accessible
-
Set up scheduled runs:
- Already configured in workflow file (4x daily cron)
- Verify cron schedule is correct for your timezone
Troubleshooting
Issue 1: "Module not found" errors in Python
Symptom:
ModuleNotFoundError: No module named 'pydantic'
Solution:
# Ensure virtual environment is activated
source venv/bin/activate
# Reinstall requirements
pip install -r requirements.txt
# Verify installation
pip list | grep pydantic
Issue 2: API Key errors
Symptom:
Error: Invalid API key for TheSportsDB
Solution:
# Check .env file exists and has correct keys
cat .env | grep THESPORTSDB_API_KEY
# Ensure .env is being loaded
python -c "from dotenv import load_dotenv; import os; load_dotenv(); print(os.getenv('THESPORTSDB_API_KEY'))"
# If None, check .env location (should be in project root)
Issue 3: Tests fail with "fixture not found"
Symptom:
pytest tests/test_patterns.py
ERROR: fixture 'sample_m3u' not found
Solution:
# Ensure you're running tests from correct directory
cd backend/epgoat
pytest tests/test_patterns.py
# Check conftest.py exists
ls tests/conftest.py
Issue 4: Frontend won't start
Symptom:
npm run dev
Error: Cannot find module '@vitejs/plugin-react'
Solution:
# Clear node_modules and reinstall
cd frontend/admin
rm -rf node_modules package-lock.json
npm install
# Try again
npm run dev
Issue 5: LLM matching not working
Symptom:
[ERROR] Claude API error: 401 Unauthorized
Solution:
# Verify API key is set correctly
echo $ANTHROPIC_API_KEY
# Test API key manually
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-3-5-haiku-20241022","max_tokens":10,"messages":[{"role":"user","content":"Hi"}]}'
# If 401, regenerate API key at console.anthropic.com
Issue 6: Regex pattern not matching
Symptom: Channel "NFL 01: Cowboys vs Giants" not matching.
Debug Steps:
# Test regex pattern in Python REPL
import re
pattern = r'^NFL\s+\d+'
channel = "NFL 01: Cowboys vs Giants"
match = re.match(pattern, channel)
print(match) # Should print match object
# If None, adjust pattern:
pattern = r'^NFL\s+\d+:' # Added colon
match = re.match(pattern, channel)
print(match) # Should now match
Fix: Update pattern in backend/epgoat/domain/patterns.py and add test case.
Issue 7: Database connection errors (D1)
Symptom:
Error: Supabase database not found
Solution:
# This error occurs in production, not local development
# For local development, use SQLite file
# Create local SQLite database
sqlite3 dist/epgoat.db < schema.sql
# Update connection string in config
export DATABASE_URL=sqlite:///dist/epgoat.db
# For production D1:
# - Ensure Cloudflare account is set up
# - Create Supabase database via wrangler CLI
# - Deploy schema.sql to D1
Next Steps
For New Developers
After completing this quick start, explore these areas:
- Read Core Documentation:
TECH_STACK.md- Understand technology decisionsAPI_SPECIFICATION.md- Learn API endpoints-
schema.sql- Understand database structure -
Explore Codebase:
- Read
backend/epgoat/domain/patterns.py- Pattern matching logic - Read
backend/epgoat/services/llm_matcher.py- LLM integration -
Read
frontend/admin/src/pages/Dashboard.tsx- Admin UI -
Pick a Starter Task:
- Add support for a new league (low complexity)
- Fix a failing test (medium complexity)
-
Implement a new API endpoint (high complexity)
-
Join Team Communication:
- Slack channel:
#epgoat-dev - Weekly standup: Mondays 10am ET
- Code review: All PRs require 1 approval
Contribution Guidelines
Before submitting a PR:
- β
All tests pass:
make test - β
Code is formatted:
make format - β
No linting errors:
make lint - β
Type checks pass:
make type-check - β
Branch is up to date with
main - β
Commit messages follow convention:
feat:,fix:,docs:, etc.
PR Template:
## Description
Brief description of changes.
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests pass
- [ ] Manual testing completed
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Comments added for complex logic
- [ ] Documentation updated
Learning Resources
EPGOAT-Specific: - Project Overview - Architecture Docs - API Documentation
Python: - Pydantic Docs - pytest Tutorial - Ruff Linter
React + TypeScript: - React Docs - TypeScript Handbook - MUI Components
Cloudflare: - Workers Docs - Supabase Database - R2 Storage
Auth0: - React SDK Guide - JWT Tokens
Stripe: - Subscription Guide - Webhooks
Getting Help
Stuck? Ask for help:
- Check Documentation First:
- Search this guide
- Read relevant spec documents
-
Check code comments
-
Slack Channels:
#epgoat-dev- General development questions#epgoat-support- User-facing issues-
#epgoat-infra- Infrastructure/deployment -
GitHub Issues:
- Search existing issues first
- Create new issue with template
-
Tag with appropriate labels
-
Code Review:
- Open draft PR early for feedback
- Tag specific reviewers for expertise
- Respond to comments promptly
Office Hours: - Tuesdays 2-3pm ET - Frontend questions (Sarah) - Wednesdays 11am-12pm ET - Backend questions (Mike) - Thursdays 3-4pm ET - Infrastructure questions (Alex)
Welcome to the EPGOAT team! π
Last Updated: 2025-10-30
Maintainer: Engineering Team
Questions? Slack: #epgoat-dev