Stadium enforces strict time-based restrictions for M2 Incubator Program activities based on the 6-week program timeline.
The M2 program runs for 6 weeks after the hackathon ends:
Hackathon End Date
↓
┌───────┬───────┬───────┬───────┬───────┬───────┐
│ Week1 │ Week2 │ Week3 │ Week4 │ Week5 │ Week6 │
└───────┴───────┴───────┴───────┴───────┴───────┘
↑ ↑ ↑ ↑
│ │ │ │
Building Phase Roadmap Submission Window
(edit roadmap) Locks (submit deliverables)
const getCurrentWeek = (hackathonEndDate) => {
const now = new Date();
const daysSince = Math.floor((now.getTime() - hackathonEndDate.getTime()) / (1000 * 60 * 60 * 24));
return Math.floor(daysSince / 7) + 1;
};Endpoint: PATCH /api/projects/:id/m2-agreement
When Allowed:
- ✅ Weeks 1-4: Teams can edit core features, documentation, and success criteria
- ✅ Admins: Can edit anytime (bypass restriction)
When Blocked:
- ❌ Week 5+: Roadmap is locked
- Error Response:
{ "status": "error", "message": "Cannot edit M2 agreement after Week 4. Currently in Week 5. Contact your mentors in your team chat if you need to make any changes." }
Implementation:
// server/api/controllers/project.controller.js
async updateM2Agreement(req, res) {
// ... validation ...
// Check week restriction (unless admin)
if (project.hackathon?.endDate && !isAdmin) {
const currentWeek = getCurrentWeek(project.hackathon.endDate);
if (!canEditM2Agreement(project.hackathon.endDate)) {
return res.status(400).json({
status: 'error',
message: `Cannot edit M2 agreement after Week 4. Currently in Week ${currentWeek}. Contact your mentors in your team chat if you need to make any changes.`
});
}
}
// ... proceed with update ...
}Endpoint: POST /api/projects/:id/submit-m2
When Allowed:
- ✅ Weeks 5-6: Teams can submit GitHub repo, demo video, docs, and summary
When Blocked:
- ❌ Weeks 1-4: Submission window not open yet
- Error:
"Submission window opens in Week 5"
- Error:
- ❌ Week 7+: Submission deadline passed
- Error:
"Submission deadline has passed. Contact WebZero for an extension."
- Error:
Implementation:
// server/api/controllers/project.controller.js
async submitM2Deliverables(req, res) {
// ... validation ...
// Check submission window (Weeks 5-6)
if (project.hackathon?.endDate) {
const currentWeek = getCurrentWeek(project.hackathon.endDate);
if (currentWeek < 5) {
return res.status(400).json({
success: false,
error: 'Submission window opens in Week 5'
});
}
if (currentWeek > 6) {
return res.status(400).json({
success: false,
error: 'Submission deadline has passed. Contact WebZero for an extension.'
});
}
}
// ... proceed with submission ...
}After running node server/update-m2-dates.js, the test projects have realistic dates:
- Status:
building - Current Week: 3 of 6
- Hackathon Ended: October 25, 2025 (2 weeks ago)
- Allowed Actions:
- ✅ Edit M2 roadmap
- ❌ Submit M2 deliverables (not Week 5 yet)
- Status:
under_review - Current Week: 6 of 6
- Hackathon Ended: October 4, 2025 (5 weeks ago)
- Allowed Actions:
- ❌ Edit M2 roadmap (past Week 4)
- ✅ Submit M2 deliverables
# Connect wallet as team member
# Visit: http://localhost:8080/projects/polkadot-portfolio-tracker-a1b2c3
# Click "Edit Roadmap" button
# Should open modal and allow editing ✅# Same project, try to submit M2 deliverables
# Should show error: "Submission window opens in Week 5" ❌# Connect wallet as team member
# Visit: http://localhost:8080/projects/decentralized-voting-dao-d4e5f6
# Click "Edit Roadmap" button
# Should show locked state and error message 🔒# Same project, click "Submit Deliverables"
# Should open modal and allow submission ✅# Connect with admin wallet
# Can edit ANY project's roadmap at ANY time
# Admin check: wallet in ADMIN_WALLETS env variableAll enforcement happens AFTER wallet authentication:
router.patch('/:id/m2-agreement',
authMiddleware, // ← Check wallet signature first
requireTeamMemberOrAdmin, // ← Then check permissions
async (req, res) => {
// ← Then check week restrictions
}
);Admins can bypass week restrictions:
const isAdmin = req.user?.isAdmin; // From auth middleware
if (project.hackathon?.endDate && !isAdmin) { // ← Admins skip check
const currentWeek = getCurrentWeek(project.hackathon.endDate);
// ... enforce restrictions ...
}If hackathon.endDate is not set:
if (project.hackathon?.endDate) { // ← Only enforce if date exists
// ... check restrictions ...
}
// Otherwise, allow the action (graceful degradation)currentWeek = Math.floor(daysSinceHackathon / 7) + 1| Days Since Hackathon | Current Week | M2 Roadmap | M2 Submission |
|---|---|---|---|
| 0-6 days | Week 1 | ✅ Editable | ❌ Too early |
| 7-13 days | Week 2 | ✅ Editable | ❌ Too early |
| 14-20 days | Week 3 | ✅ Editable | ❌ Too early |
| 21-27 days | Week 4 | ✅ Editable | ❌ Too early |
| 28-34 days | Week 5 | ❌ Locked | ✅ Allowed |
| 35-41 days | Week 6 | ❌ Locked | ✅ Allowed |
| 42+ days | Week 7+ | ❌ Locked | ❌ Too late |
All date helpers are in server/api/utils/dateHelpers.js:
const getCurrentWeek = (hackathonEndDate) => {
const now = new Date();
const endDate = new Date(hackathonEndDate);
const daysSince = Math.floor((now.getTime() - endDate.getTime()) / (1000 * 60 * 60 * 24));
return Math.floor(daysSince / 7) + 1;
};const canEditM2Agreement = (hackathonEndDate) => {
const currentWeek = getCurrentWeek(hackathonEndDate);
return currentWeek >= 1 && currentWeek <= 4;
};const isSubmissionWindowOpen = (hackathonEndDate) => {
const currentWeek = getCurrentWeek(hackathonEndDate);
return currentWeek >= 5 && currentWeek <= 6;
};The frontend also displays week-based UI elements:
// client/src/components/M2AgreementSection.tsx
const currentWeek = useMemo(() => {
if (!project?.hackathon?.endDate) return null;
return calculateCurrentWeek(new Date(project.hackathon.endDate));
}, [project]);
const canEdit = currentWeek && currentWeek >= 1 && currentWeek <= 4;{canEdit && (isTeamMember || isAdmin) && (
<Button onClick={onEdit}>
<Edit className="mr-2 h-4 w-4" />
Edit Roadmap
</Button>
)}
{!canEdit && currentWeek && currentWeek > 4 && (
<Alert>
<Lock className="h-4 w-4" />
<AlertDescription>
Roadmap locked after Week 4. Contact your mentors in your team chat if you need to make any changes.
</AlertDescription>
</Alert>
)}// client/src/components/SubmissionTimeline.tsx
{currentWeek < 5 && (
<p>Submission opens in Week 5 ({daysUntilWeek5} days)</p>
)}
{currentWeek >= 5 && currentWeek <= 6 && (
<Button>Submit Deliverables</Button>
)}
{currentWeek > 6 && (
<Alert variant="destructive">
Submission deadline passed. Contact WebZero for extension.
</Alert>
)}# server/.env
ADMIN_WALLETS=5Di7WRCjywLjV53hVjdBekPo2mLtyZAxQYenvW1vKfMNCyo9Each project must have hackathon.endDate set:
{
hackathon: {
id: "synergy-2025",
name: "Synergy 2025",
endDate: new Date("2025-07-18T18:00:00"), // ← Required for enforcement
eventStartedAt: "synergy-hack-2024"
}
}The one-shot update-m2-dates.js script was removed in the 2026-04-14 cleanup after it ran. If projects ever need their hackathon.endDate patched again, restore it from git history (git show 62928dd^:server/scripts/update-m2-dates.js) or apply the update directly via the repository layer in server/api/repositories/project.repository.js.
| Feature | Status | Details |
|---|---|---|
| Week Calculation | ✅ Implemented | getCurrentWeek() in dateHelpers.js |
| M2 Roadmap Lock | ✅ Enforced | Weeks 1-4 only (unless admin) |
| M2 Submission Window | ✅ Enforced | Weeks 5-6 only |
| Admin Bypass | ✅ Implemented | Admins can edit anytime |
| Frontend UI Updates | ✅ Implemented | Shows locks, week numbers, status |
| Error Messages | ✅ Clear | Tells users current week and why blocked |
| Test Data | ✅ Updated | Run update-m2-dates.js for testing |
| Documentation | ✅ Complete | This file + API docs |
The date enforcement is FULLY FUNCTIONAL and production-ready! 🎉
If teams need to make changes after Week 4 or submit after Week 6:
- They see clear error messages in the UI
- Error messages tell them to "Contact your mentors in your team chat"
- Admins can make changes on their behalf using admin wallet
For emergencies: Admins can connect with ADMIN_WALLETS and bypass all restrictions.
Last updated: November 8, 2025 Status: ✅ Production Ready