Messaging in Red Stet
Three messaging surfaces: per-assignment 1:1 threads, per-assignment updates that fan out to the whole class, and class-wide announcements not tied to any assignment. How each works and when to reach for which.
- Three surfaces, one mental model
- The per-assignment thread
- Who can see a thread
- Class-wide assignment updates
- Class-wide announcements
- Notifications and the bell
- Muting notification kinds
- The teacher inbox
- Unread and mark-read
- Who can send what
- What a TA can do in messages
- What a student sees
- When to use which
Three surfaces, one mental model
Each surface answers a different question.
Per-assignment 1:1 thread
Private conversation between one student and teaching staff, scoped to an assignment. "I'm stuck on question 3" belongs here. Other students never see it.
Class-wide assignment update
One-to-many post anchored to a specific assignment. assignmentUpdates.create. "Due date moved to Friday" belongs here.
Class-wide announcement
One-to-many post anchored to the class, no assignment required. classroomAnnouncements.create. "Reading for next week is in your inbox" or "Lab safety briefing Friday" belongs here.
The per-assignment thread assignmentMessages
One private thread per (assignment, student). Created lazily — the first message brings it into existence; assignments with no questions stay clean.
From the student side, the thread lives in the assignment info panel under the Messages tab. Open an assignment, switch tabs, type, send. Student messages are right-aligned red; teacher messages left-aligned grey.
From the teacher side, reach it two ways: the Messages button next to the student's submission on the grading view, or the teacher inbox which aggregates every thread across every assignment you own or staff.
Sending fans out a notification to the other party. Sender is implicitly read; recipient counts as unread until they open the thread.
Who can see a thread
The thread for assignment X + student Y is visible to:
- Student Y — their own thread, no others.
- The assignment's owner — every student's thread for this assignment.
- Co-teachers and TAs with
gradeSubmissions— same teacher-side view.
The check is enforced server-side in assignmentMessages.create. With gradeSubmissions off, the UI still shows the thread but the send button errors.
Other students never see this thread. Even with roster visibility on, peers see "who's enrolled," not "messages with the teacher."
Removed staff lose access immediately. Authorship on old messages stays (senderUserId is permanent); they can't read or post.
| Viewer | Reads | Replies |
|---|---|---|
| Student Y (the student of record) | Yes | Yes |
| Owner of the assignment | Yes | Yes |
| Co-teacher (with grade rights) | Yes | Yes |
| TA (with grade rights) | Yes | Yes |
| Other student in same class | No | No |
| Removed staff | No | No |
Class-wide assignment updates assignmentUpdates
Post an assignment update from the assignment view's Updates section. Every active enrollee gets a notification titled Update on "<assignment>" with a snippet. The update also lands in the assignment's Stream entry and each student's info panel.
Use it for:
- "Typo in question 3 — corrected version is now live."
- "Due date moved to Friday 11:59."
- "I uploaded a graphic-organizer template — link in the assignment description."
Updates are text-only. Files: attach to the assignment itself, a class-wide announcement, or a 1:1 thread.
Class-wide announcements classroomAnnouncements
A class-wide post not tied to any assignment. Scope is the whole class; anchor is the class itself.
Compose from the Stream tab. The + Post announcement disclosure opens a textarea with inline attachment chips. Paste, attach if needed, Post. The card lands at top of Stream and fans out to every recipient in the same transaction.
Recipients
Every active student and active staff member (owner, co-teachers, TAs) gets a bell entry (kind: 'classroom-announcement', deep-linked to the class) plus a transactional email with the body snippet and "Open in Red Stet" button. The author is excluded.
Who can post
Gated by postClassMessages. Owners and co-teachers on by default; TAs off by default but grantable per-class from the People tab. Students don't see the composer.
Attachments
Same pipeline as assignment attachments: signed PUT URL via classroomAnnouncements.generateAttachmentUploadUrl, bytes straight to Convex storage, storageId embedded in the row. Recipients click chips to download via signed URLs from classroomAnnouncements.getAttachmentUrls.
Editing and deleting
The author or classroom owner can soft-delete from the Stream card. The row stays for audit; disappears from every recipient's feed. Already-fanned-out notifications aren't retracted. No edit-in-place — delete and repost.
Heads up — the lab safety briefing moved to Friday. Bring the worksheet I attached.
Notifications and the bell notifications
Every message and every update fans out one row per recipient into the notifications table. The bell flyout reads from this feed.
Each row carries:
title— sender + assignment for thread messages;Update on "<assignment>"for updatesbody— 120-character snippetrefKind+refId— deep-link targetclassroomId— color-coding to source class
Clicking opens the assignment with the right tab pre-selected. Read state is tracked per-user on the notification row, independent of the message's readBy array — dismissing the bell doesn't mark the thread read; opening the thread doesn't clear unrelated bell items.
Email mirror
Every thread message also emails the recipient: subject "Sender: Assignment", body carrying sender name, classroom and assignment context, snippet, and deep link. Attachments are mentioned; the files stay in Red Stet behind auth.
Class-wide announcements mirror the same way — every recipient gets a transactional email with snippet and class deep link.
One source of truth: bell + email fire from the same mutation. Sends are best-effort — a failed SMTP attempt logs without rolling back the message or in-app notifications.
Muting notification kinds users.notificationMuteKinds
Customizations → Notifications has a toggle per kind. Off suppresses the bell entry for that kind.
Six muteable kinds:
- Assignment updates (
assignment-update) — teacher follow-ups on an assignment. - Assignment messages (
assignment-message) — replies on your 1:1 thread. - Classroom announcements (
classroom-announcement) — class-wide posts. - Assignment reminders (
assignment-reminder) — teacher nudges about outstanding work. - Submission graded (
submission-graded) — your work was graded. - Submission returned (
submission-returned) — teacher returned a submission for revision.
Mute only suppresses the bell — the underlying messages, updates, and reminders still land in their own surfaces. Email mirrors are governed separately.
Stored in users.notificationMuteKinds; mutes follow you across devices.
Settings sync across devices via Convex.
The teacher inbox
The teacher view has two tabs: Classrooms and Inbox. The Inbox is your cross-class triage — one row per (assignment, student) thread, sorted by most-recent activity. Unread rows carry a red left-border and count badge.
Each row: student name, assignment title, latest snippet, unread count. Click to open in the message modal; opening calls markThreadRead and clears the badge.
The inbox aggregates every assignment you own or staff (co-teacher or TA). A co-teacher on three classes sees the union. assignmentMessages.listForTeacherInbox dedupes.
A top-bar badge shows total unread thread messages (assignmentMessages.unreadCountForMe). Separate from the bell badge, which is broader than messages.
Unread and mark-read
Each message row carries a readBy array. Opening a thread calls markThreadRead, appending the viewer's ID to every unread row. Senders are implicitly read on their own rows.
That powers:
- Unread badge on the teacher inbox row and top-bar pill
- Unread badge on the student-side Messages tab
- Cross-thread total on the dashboard chip
Mark unread
Any participant can flip a thread back to unread. Two places:
- On the teacher inbox row — Mark unread for read threads, Mark read to dismiss without opening.
- Inside the open thread modal — header Mark unread button closes and re-flags.
markThreadUnread strips the caller's ID from every readBy array except rows they sent. Use it for "I'll come back to this" — read during triage, surface again tomorrow.
Who can send what
Two separate capabilities gate the two surfaces:
gradeSubmissions— required to reply on a per-student thread. Same capability that gates the rubric and recordings.postClassMessages— required to post an assignment update.
Defaults baked into the role:
| Action | Owner | Co-tch. | TA | Student |
|---|---|---|---|---|
| Send a 1:1 thread message to themselves | — | — | — | Yes |
| Reply on a student's 1:1 thread | Yes | Yes | Yes | — |
Post an assignment update (postClassMessages) | Yes | Yes | No | — |
Post a class-wide announcement (postClassMessages) | Yes | Yes | No | — |
| View every student's thread on an assignment | Yes | Yes | Yes | — |
What a TA can do in messages
TA defaults: gradeSubmissions on, postClassMessages off:
- Replies on per-student threads — yes. Same as the teacher. Reply is attributed to the TA by name.
- Class-wide broadcast — no by default. No assignment updates, no class-wide announcements. The composer is hidden.
Flip Post class messages on for the TA role in the People tab permissions panel. Takes effect immediately; no save button.
Attribution is permanent. A TA removed from the class keeps their name on old messages; they just can't post new ones.
What a student sees
Messaging lives inside the assignment — no separate top-level surface. Open an assignment, switch to Messages, type, send. That's the flow.
You see one thread per assignment — yours. No other students' threads, no teacher inboxes.
Where notifications land
Teacher replies and assignment updates land in your bell feed. Each deep-links into the assignment with the right tab pre-selected.
What you can attach
+ Attach sits next to Send. Files upload as chips above Send. Hit Send and they go with the message. Teacher can attach the same way. Attachments render as chips under each bubble — click to download.
Attachments without body text are allowed.
What you can't do
- Post into another student's thread — only the student of record can.
- DM the teacher outside an assignment. No unanchored channel. For administrative questions with no relevant assignment, email is the right channel.
When to use which
- One student needs an answer about their work → 1:1 thread. Attach files if relevant.
- Whole class, specific assignment → assignment update.
- Whole class, not about an assignment → class-wide announcement from Stream.
- Send a file to one student → attach to the 1:1 thread.
- Send a file to every student → attach to the assignment, or to a class-wide announcement.
Every message has a context. 1:1 thread → (assignment, student). Assignment update → assignment. Class-wide announcement → classroom.
| If you want to… | Use |
|---|---|
| Answer one student's question | 1:1 thread |
| Tell everyone about an assignment change | Assignment update |
| Tell everyone something not tied to one assignment | Class-wide announcement |
| Send a file to one student | Attach to a 1:1 thread message |
| Send a file to every student | Attach to the assignment or to a class-wide announcement |
Related
→ Creating an assignment — where the assignment-update composer lives.
→ Co-teachers & TAs — the gradeSubmissions and postClassMessages capabilities in context.
→ Setting up your first class — the People tab, where per-class permission overrides live.
→ Back to the help library for more topics.
Missing something? Email feedback.