Skip to content

/vetting — Slash Command Reference

Commands

In workflow order:

/vetting session timeful [link]
/vetting session timeful-refresh [link]
/vetting session info
/vetting session new [date] [time]
/vetting session add [person]
/vetting session remove [person]
/vetting session announce
/vetting session ingest

General Notes

Vetting_sessions schema — writable fields relevant to the bot:

  • vetting_datetime — DateTime (null until /vetting session new is run)
  • vettees — ReferenceList("People") — write target for vettees
  • vetting_panel — ReferenceList("People") — write target for panel members
  • no_shows — ReferenceList("People")
  • notes — Text
  • timeful_url — Text — Timeful poll URL, written by timeful
  • timeful_respondents — Text (JSON) — {"display_name": people_row_id_or_null}, written by timeful-refresh
  • schedule_message_id — Text — Discord message ID of the scheduling post, written by timeful
  • announcement_message_id — Text — Discord message ID of the session announcement, written by announce

vettees_person_id is a read-only formula that returns rec.vettees.person_id — it is not a write target. Do not write to it.

Vetting_session_notes and Prevetting_vetting_notes are both accessible to the bot editor account. Notes are kept vague enough that neither table is considered PII-restricted for bot access purposes.

Session lifecycle

A Vetting_sessions row is created by /vetting session timeful at the start of planning, with vetting_datetime null. /vetting session new patches vetting_datetime onto that row once a time is confirmed. If no unscheduled row exists when new runs (backfill), a new row is created directly.


/vetting session timeful

Signature

/vetting session timeful [link]

Access

Admin (unvetted server)

Purpose

Starts session planning: creates a Vetting_sessions row, posts a vetting availability poll announcement to the announce channel using templates/vetting/schedule.md, and stores the Timeful URL and scheduling post message ID on the row. The operator creates the Timeful poll manually first, then passes the link to this command. due_date is computed as 25 hours before midnight of the poll's first date (e.g. first date 5/27 → due 5/25 11:00 PM). Once responses are in and a time is chosen, use /vetting session new to set the date.

Parameters

  • link — URL of the manually-created availability poll

Timeful API

Timeful exposes a public unauthenticated REST endpoint — no API key, no headless browser needed:

GET https://timeful.app/api/events/{short_id}

Returns: poll name, date range (dates[]), respondent names and count (responses{}), and a scheduledEvent field (non-null if the organizer has locked in a time). The short ID is the last path segment of the poll URL (e.g. fAfBA from https://timeful.app/e/fAfBA). See scratch/timeful_test.py for a working example.

Flow

  1. Call Timeful API with the provided link to confirm it resolves and pull the date range
  2. Create a Vetting_sessions row with timeful_url set
  3. Post announcement to announce channel (non-ephemeral) using schedule.md with {{date_range}}, {{timeful_url}}, {{due_date}}, {{unvetted_role}} substituted
  4. PATCH Vetting_sessions.schedule_message_id with the posted message ID

/vetting session timeful-refresh

Signature

/vetting session timeful-refresh [link]

Access

Admin (unvetted server)

Purpose

Fetches current responses from a Timeful poll and shows a linking UI. Respondents already in Vetting_sessions.timeful_respondents are shown as ✅ with no action needed. New respondents get a UserSelect to link to a guild member. On confirm, writes $People.last_schej_RSVP for all linked respondents and persists the updated name→ID map. link is optional — defaults to the URL stored on the current upcoming session.

Parameters

  • link — URL of the Timeful poll (optional; defaults to Vetting_sessions.timeful_url)

last_schej_RSVP field

$People.last_schej_RSVP stores the date the member last submitted Timeful availability. The value written is the first date in the poll's dates[] array (i.e. the start of the date range, not the submission date).

Flow

  1. Call GET https://timeful.app/api/events/{short_id} — extract respondent names and dates[0] as the reference date
  2. Load Vetting_sessions.timeful_respondents (stored name→ID map) for the current upcoming session
  3. Classify each respondent: already in stored map with a non-null ID → ✅ already linked; otherwise → needs manual link
  4. Post ephemeral response with already-linked respondents (sorted by days waiting) and a UserSelect for each unlinked name (up to 4):
📋 Timeful responses (8 respondent(s))

  ✅ .morgan.le.fae → Display Name (@username) (104d)
  ✅ shakethespear  → Display Name (@username) (75d)
  ⚠️ Alexmkzero    — unlinked
  ⚠️ K8            — unlinked
  ...
  1. On confirm: PATCH $People.last_schej_RSVP for all linked respondents (already-linked + newly selected); update timeful_respondents with new links

Linking UI

For each unlinked respondent (up to 4), show a Discord UserSelect labeled with the Timeful display name. Operator picks the guild member; bot resolves to $People via snowflake. Dismiss button makes no changes.

Notes

  • last_schej_RSVP is a Grist Date column. Write dates[0] parsed to a datetime.date, converted via _to_unix() (noon UTC).
  • Can be run multiple times; already-linked names are never re-asked. New respondents who joined the poll since the last run will appear as unlinked.

/vetting session new

Signature

/vetting session new [date] [time]

Access

Admin (unvetted server)

Purpose

Sets the date and time for the upcoming session. If an unscheduled Vetting_sessions row exists (created by timeful), patches its vetting_datetime. If no unscheduled row exists (backfill case), creates a new row.

Parameters

  • date — parsed flexibly (e.g. 6/14, June 14, 2025-06-14)
  • time — 12h or 24h format (e.g. 6pm, 18:00), combined with date into vetting_datetime

Response

Ephemeral:

✅ Vetting session scheduled — Sat Jun 14 2025, 6:00 PM
Use /vetting session add to add vettees.

/vetting session add

Signature

/vetting session add [person]

Access

Admin (unvetted server)

Purpose

Adds a vettee to the upcoming vetting session and creates a row for them in Vetting_session_notes so note-takers can record session notes without Grist access. If a session announcement has been posted, edits it to reflect the updated vettee list. The person param autocompletes from all guild members.

"Upcoming session" definition

The nearest Vetting_sessions row with a future vetting_datetime, or the unscheduled row if no scheduled session exists yet.

Flow

  1. Resolve person param (Discord snowflake from autocomplete) → $People record via resolve_member
  2. Find upcoming session
  3. PATCH Vetting_sessions.vettees to append the person (ReferenceList)
  4. Create a row in Vetting_session_notes with validated_identity set to the person
  5. Edit the announcement post if announcement_message_id is set on the session
  6. Reply ephemeral confirmation

Response

✅ alice added to vetting session (Jun 14 2025, 6:00 PM)

/vetting session remove

Signature

/vetting session remove [person]

Access

Admin (unvetted server)

Purpose

Removes a vettee from the upcoming session. The person param autocompletes from all guild members; the command validates the selected person is in the session. Edits the announcement post if one has been posted.

Response

✅ alice removed from vetting session.

/vetting session announce

Signature

/vetting session announce

Access

Admin (unvetted server)

Purpose

Posts the session announcement to the announce channel using templates/vetting/session.md, with {{session_datetime}}, {{vettees_list}}, and {{attendee_role}} substituted. Stores the message ID in Vetting_sessions.announcement_message_id so subsequent add/remove calls can edit it in place.

Response

Rendered templates/vetting/session.md posted to announce channel; ephemeral ✅ Announcement posted. to caller.


/vetting session ingest

Signature

/vetting session ingest

Access

Admin (unvetted server)

Purpose

After a session concludes, moves notes from Vetting_session_notes into Prevetting_vetting_notes, linked to the correct vettees.

Why two tables

Vetting_session_notes is a public Grist table — note-takers who don't have a Grist account can still enter notes there during the session. Ingest links them to the right vettees in Prevetting_vetting_notes after the fact.

Flow

  1. Find the most recent past session (nearest Vetting_sessions row with vetting_datetime in the past)
  2. For each person in vettees: fetch their Vetting_session_notes row (matched by validated_identity) and their existing Prevetting_vetting_notes row (matched by validated_username)
  3. PATCH Prevetting_vetting_notes with the notes content using the field mapping below
  4. Reply with a summary

Field mapping

Vetting_session_notes Prevetting_vetting_notes
want_to_go_by What_is_the_name_you_would_like_to_go_by_during_the_vetting_session_handle_alias_name_all_ok_
pronouns What_are_your_pronouns_
what_inspired What_inspired_to_join
primary_goals Training_goals
value_to_org Value_to_org
activism_ Activism_
support_system Support_system
socialist_org Socialist_org
civil_nonsectarian Civil_nonsectarian
vetting_notes Vetting_Notes
prevetting Prevetting
ideology Ideology
persec_opsec Persec_opsec
validated_identity validated_username

Response

✅ Vetting session ingested (Jun 14 2025, 6:00 PM)
  Notes moved for: alice, bob, carol

Notes

  • Vetting_session_notes has no Reference("Vetting_sessions") — ingest finds notes by matching validated_identity against the session's vettees list.
  • No ingested marker is written to Vetting_sessions — the operator is responsible for running ingest only once per session.
  • Idempotency: running ingest twice will overwrite Prevetting_vetting_notes fields with the same values, which is harmless.

/vetting session info

Signature

/vetting session info

Access

Admin (unvetted server)

Purpose

Shows a summary of the most recently created vetting session. Fetches fresh Timeful data for the date range, reads the stored timeful_respondents map for match display, and shows vettees and post links.

Flow

  1. Find the most recently created Vetting_sessions row (by row ID, not datetime)
  2. Display:
  3. vetting_datetime (or "not yet scheduled") — flag with ⚠️ and help text if in the past
  4. Timeful poll link
  5. Links to scheduling post (schedule_message_id) and announcement post (announcement_message_id)
  6. If timeful_url is set, call the Timeful API for the current date range; read timeful_respondents for match state:
  7. Matched respondents: ✅ name → display (@username) (Nd), sorted by days waiting desc
  8. Unmatched: ⚠️ name — unmatched/unlinked
  9. Vettees list with Discord mentions

Response

Ephemeral summary to caller.


Vetting Notes — Approach

Vetting_session_notes acts as a staging table during the session (public, accessible without a Grist account). /vetting session ingest links those notes to the correct vettees in Prevetting_vetting_notes after the fact. The bot editor account has access to both tables.


Future work

  • No-show logging/vetting session noshow [@member] to PATCH Vetting_sessions.no_shows and optionally trigger a Status_actions row.
  • Vetting completion/vetting pass [@member] to add a Status_actions row with status="vetted". The passed_vetting_button action in $People shows exactly what that write looks like.