Status: PR-Ready Concept
Feature Name: Seasons & Templates (internal codename: seasons-v1)
Seasons & Templates transforms the talk submission system from a simple form into a repeatable event management platform. Even for a single meetup like Rust NYC, this feature future-proofs the system by organizing submissions into time-bound "seasons" with configurable deadlines, reusable templates, and automatic archival.
| Pain Point Today | How Seasons Solves It |
|---|---|
Channel names use nodate- prefix (see backend/index.ts:79) | Channels include event date: 2026-mar-15-rust-nyc-talk |
| No deadline enforcement | Clear CFP open/close dates displayed to submitters |
| Old submissions clutter the Discord category | Auto-archive past season channels to archive category |
| No way to compare submission volumes across events | Built-in per-season analytics |
| Setup is manual each time | Templates capture your proven format for 1-click season creation |
/admin β Templates β Create Template
Template Name: "Monthly Rust NYC Meetup"
Default Duration: 4 weeks CFP window
Channel Naming: {date}-{submission_id}-{speaker}
Welcome Message: [Custom markdown with {{speaker}}, {{deadline}} variables]
Organizer Roles: @rust-nyc-organizers
Archive Category: "Past Events"
/admin β Seasons β New Season
Select Template: "Monthly Rust NYC Meetup"
Event Name: "Rust NYC March 2026"
Event Date: March 15, 2026
CFP Opens: February 1, 2026
CFP Closes: March 1, 2026 11:59pm EST
[Create Season]
The submission form now shows:
March 2, 2026 12:00am:
βββ Form shows: "CFP closed for March event. Next event: April 2026"
βββ Existing channels remain active for speaker coordination
βββ Discord channels moved to archive category after event date
| Capability | Description |
|---|---|
| Multi-season support | Run overlapping CFPs (March event open while finalizing February) |
| State machine | draft β open β closed β event_complete β archived |
| Deadline enforcement | Form blocks submissions after cfp_closes_at |
| Timezone-aware | Store all dates in UTC, display in organizer's configured TZ |
| Capability | Description |
|---|---|
| Reusable configurations | Save CFP duration, messages, Discord settings |
| Variable substitution | {{speaker}}, {{deadline}}, {{event_name}}, {{event_date}} |
| Template versioning | Copy template before editing to preserve history |
| Quick clone | "Create next month's event from this season" |
| Capability | Description |
|---|---|
| Per-season categories | Each season gets its own Discord category |
| Date-prefixed channels | 2026-mar-15-rust-nyc-talk instead of nodate-1-speaker |
| Auto-archive | Move channels to archive category after event_date + 7 days |
| Deadline reminders | Optional bot message: "CFP closes in 48 hours!" |
| Metric | Query |
|---|---|
| Submissions per season | SELECT season_id, COUNT(*) FROM submissions GROUP BY season_id |
| Submission velocity | Daily submission rate during CFP window |
| Conversion funnel | Submissions β Scheduled β Presented |
| Template performance | Which templates generate more submissions? |
-- Templates table
CREATE TABLE season_templates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
default_cfp_duration_days INTEGER DEFAULT 28,
channel_name_pattern TEXT DEFAULT '{date}-{id}-{speaker}',
welcome_message_template TEXT,
discord_category_pattern TEXT,
archive_category_id TEXT,
organizer_role_ids TEXT, -- JSON array
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Seasons table
CREATE TABLE seasons (
id INTEGER PRIMARY KEY AUTOINCREMENT,
template_id INTEGER REFERENCES season_templates(id),
name TEXT NOT NULL,
slug TEXT NOT NULL UNIQUE,
event_date DATE NOT NULL,
cfp_opens_at DATETIME NOT NULL,
cfp_closes_at DATETIME NOT NULL,
timezone TEXT DEFAULT 'America/New_York',
status TEXT DEFAULT 'draft' CHECK(status IN ('draft','open','closed','event_complete','archived')),
discord_category_id TEXT,
discord_archive_category_id TEXT,
metadata TEXT, -- JSON for extensibility
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Modify existing submissions table
ALTER TABLE talk_submissions_3 ADD COLUMN season_id INTEGER REFERENCES seasons(id);
Current (backend/index.ts:79):
const channelName = `nodate-${submissionId}-${sanitizeChannelName(speakerName)}`;
Proposed:
const season = await getActiveSeason();
const eventDate = format(season.event_date, 'yyyy-MMM-dd').toLowerCase();
const channelName = `${eventDate}-${submissionId}-${sanitizeChannelName(speakerName)}`;
// Result: "2026-mar-15-42-alice-smith"
GET /api/admin/templates - List all templatesPOST /api/admin/templates - Create templatePUT /api/admin/templates/:id - Update templateDELETE /api/admin/templates/:id - Delete templateGET /api/seasons - List seasons (public: shows active/upcoming)GET /api/seasons/active - Get currently accepting seasonPOST /api/admin/seasons - Create season from templatePUT /api/admin/seasons/:id - Update seasonPOST /api/admin/seasons/:id/transition - Change statusPOST /api/submissions - Now requires active season, auto-assigns season_idGET /api/submissions?season_id=X - Filter by season// frontend/components/TalkSubmissionForm.tsx
// Add season context display
<div className="season-banner">
<h2>{activeSeason.name}</h2>
<p>Event Date: {formatDate(activeSeason.event_date)}</p>
<p>Submissions close: {formatDeadline(activeSeason.cfp_closes_at)}</p>
<CountdownTimer deadline={activeSeason.cfp_closes_at} />
</div>
// Form disabled state when CFP closed
{!activeSeason && (
<div className="cfp-closed">
<p>No events are currently accepting talk submissions.</p>
<p>Next event: {nextSeason?.name || 'TBA'}</p>
</div>
)}
/admin
βββ /admin/seasons # Season list with status badges
βββ /admin/seasons/:id # Season detail + submission list
βββ /admin/seasons/new # Create from template
βββ /admin/templates # Template management
βββ /admin/analytics # Cross-season metrics
| Metric | Target | Measurement |
|---|---|---|
| Submission increase | +20% vs pre-deadline baseline | Compare submission velocity in final 48h of CFP |
| Organizer time saved | 30 min/event | Survey: time to set up new event |
| Template reuse rate | >80% of seasons use templates | seasons with template_id / total seasons |
| Archive completion | 100% auto-archived within 14 days | Cron job success rate |
| Deadline compliance | 0 post-deadline submissions | Form rejection count |
Rust NYC Launches Seasons & Templates: Making Community Events Repeatable
New feature brings deadline-driven CFPs and one-click event setup to the talk submission platform
Today, Rust NYC announces Seasons & Templates, a new capability in their talk submission system that transforms ad-hoc event planning into a streamlined, repeatable process. Organizers can now create reusable templates, set clear CFP deadlines, and let automation handle channel archival.
Running recurring meetups means repeating the same setup steps every month. Organizers manually create Discord categories, update welcome messages, and remind themselves when to close submissions. Submitters often miss deadlines because there's no visible countdown.
Seasons & Templates introduces:
"Before Seasons, every monthly meetup felt like starting from scratch. Now I click 'Create Season', pick next month's date, and the system handles the rest." β Rust NYC Organizer
Start using Seasons today at your next meetup. Visit [docs link] to set up your first template.
seasons and season_templates tablesseason_id to submissions tablegetActiveSeason() helperIf this concept is approved, the orchestrator should:
backend/database/migrations/001_seasons.tsbackend/services/season.ts with:
getActiveSeason(), createSeasonFromTemplate(), transitionSeasonStatus()backend/index.ts:79-85 to use season datesbackend/index.ts:79backend/index.ts:55-114backend/discord.tstalk_submissions_3 in backend/index.ts:25-34