Red Stet
← Back to Help
Help · For teachers & students · Tier 2 — Day-to-day teaching

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

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.

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.

The thread is per (assignment, student). Same student asking about a different assignment lands in a different thread. There is no global student-teacher DM.

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.

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.

Fan-out is atomic. The update row and every per-student notification land in the same Convex mutation. No bell entries pointing at a post that didn't happen.

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.

Decision rule. Whole class, not about an assignment → announcement. Whole class, about an assignment → assignment update. One student → 1:1 thread.

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 updates
  • body — 120-character snippet
  • refKind + refId — deep-link target
  • classroomId — 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.

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:

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.

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.

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.