Skip to main content

Floor Control and Speaking Flow

This document covers how the system implements Part XVI (S.O. 78–83) and Part XVII (S.O. 97) of the National Assembly Standing Orders (7th Edition) — the rules of debate, floor requests, the speaking queue, microphone control, and time limits.


1. Key Standing Orders Implemented

Standing OrderRuleSystem enforcement
S.O. 78Every Member wishing to speak shall address a request to the SpeakerPOST /sittings/:id/speaking-requests
S.O. 79When two or more Members rise at the same time, the Speaker calls upon themQueue ordering + Point of Order priority
S.O. 81No Member shall speak after the question has been putBlocked when a VoteSession is OPEN
S.O. 82No Member shall speak more than once to a questionPrior-speech check per agenda item
S.O. 82AMember who has spoken may speak to an amendmentPoint of Information requests allowed
S.O. 83Any Member may raise a Point of Order at any timePOINT_OF_ORDER type floats to queue top
S.O. 97Time limits on debate — auto-assigned by roletimeLimit set on grant from role lookup

2. Speaking Request Lifecycle

A speaking request moves through the following states:

Member submits request


PENDING ◄──── queued, visible to Speaker

Speaker acts
┌────┴────┐
▼ ▼
GRANTED DENIED ──► terminal


IN_PROGRESS ──► mic ON, timer running

├──► PAUSED (mic OFF, timer frozen) ──► IN_PROGRESS (resumed)


COMPLETED ──► terminal

PENDING / GRANTED / IN_PROGRESS ──► CANCELLED (Member withdraws)

Status Meanings

StatusMeaning
PENDINGRequest submitted, waiting for Speaker to act
GRANTEDSpeaker recognised the Member — mic not yet ON
IN_PROGRESSMic is ON, Member is speaking, timer running
DENIEDSpeaker declined the request
COMPLETEDSpeech ended (mic OFF, speakingEnd recorded)
CANCELLEDMember withdrew request

3. Request Types (S.O. 78, 83)

requestType constantStanding OrderDescription
REGULARS.O. 78Standard floor request to speak in debate
POINT_OF_ORDERS.O. 83Point of order — floats to top of queue
POINT_OF_INFORMATIONS.O. 82APoint of information — exempt from S.O. 82 repeat-speaker rule
URGENT_MATTER_MOVERS.O. 33Mover of urgent adjournment motion
URGENT_MATTER_OTHERS.O. 33Other speaker on urgent adjournment matter

4. Queue Ordering (S.O. 79)

When the Speaker views the speaking queue (GET /sittings/:id/speaking-requests), requests are sorted as follows:

  1. Points of Order (POINT_OF_ORDER) always sort to position 0 — ahead of all other requests
  2. Within each priority group, requests are ordered by queuePosition (insertion order) then by requestedAt timestamp

This directly implements S.O. 79: "If two or more Members request to speak at the same time, the Speaker shall call upon them in such order as the Speaker deems fit", with the system providing the Speaker a pre-ordered queue.


5. Standing Order Enforcement on Submission

The POST /sittings/:id/speaking-requests endpoint enforces three SO rules before creating the request:

S.O. 78 — Active Sitting Required

Throws BadRequestException if sitting.status ≠ IN_PROGRESS
"SO 78 – Speaking requests can only be made during an active sitting"

S.O. 81 — No Speaking After Question Put

Throws BadRequestException if VoteSession WHERE agendaItemId = x AND status = OPEN exists
"SO 81 – No Member may speak after the question has been put"

S.O. 82 — No Repeat Speaking

Throws BadRequestException if a prior GRANTED / IN_PROGRESS / COMPLETED request exists
for the same (userId, agendaItemId) combination.
Does NOT apply to POINT_OF_ORDER or POINT_OF_INFORMATION request types.
"SO 82 – A Member may not speak more than once to the same question"

6. Granting and Time Limits (S.O. 79, 97)

POST /sittings/speaking-requests/:requestId/grant — Speaker recognises the Member.

On grant, the service automatically resolves the timeLimit from the member's role:

Member roleS.O. 97Time limitSystem constant
leader_majority / leader_minorityS.O. 9760 minutesSPEAKING_TIME_LIMITS.LEADER_MAJORITY_MINORITY = 3600 s
member (regular debate)S.O. 9720 minutesSPEAKING_TIME_LIMITS.REGULAR_DEBATE = 1200 s
URGENT_MATTER_MOVER typeS.O. 3310 minutesSPEAKING_TIME_LIMITS.URGENCY_MOVER = 600 s
URGENT_MATTER_OTHER typeS.O. 335 minutesSPEAKING_TIME_LIMITS.URGENCY_OTHER = 300 s
Statement (S.O. 43)S.O. 433 minutesSPEAKING_TIME_LIMITS.STATEMENT = 180 s
Petition presenter (S.O. 225)S.O. 2255 minutesSPEAKING_TIME_LIMITS.PETITION = 300 s
POINT_OF_ORDER typeS.O. 83No limitSPEAKING_TIME_LIMITS.POINT_OF_ORDER = 0 (Speaker decides)

The resolved timeLimit (in seconds) and remainingTime are set on the SpeakingRequest record and sent to all clients via the micOn WebSocket event.


7. Microphone Control (S.O. 79)

After a request is GRANTED, the Speaker activates the microphone:

Mic ON — POST /sittings/speaking-requests/:requestId/mic-on

  • Sets micStatus = ON, status = IN_PROGRESS
  • Records speakingStart timestamp
  • Emits micOn WebSocket event to the sitting room with timeLimit, remainingTime, speakingStart, and serverNow

Point of Order interruption: If a POINT_OF_ORDER mic-on request is made while another Member has mic ON, the system automatically:

  1. Pauses the current speaker — sets micStatus = OFF, records remainingTime, sets pausedAt
  2. Emits micOff with event: PAUSED_FOR_POINT_OF_ORDER to the sitting room
  3. Turns on the Point of Order member's mic

Mic OFF — POST /sittings/speaking-requests/:requestId/mic-off

  • Sets micStatus = OFF, status = COMPLETED
  • Records speakingEnd
  • Emits micOff WebSocket event

Pause — POST /sittings/speaking-requests/:requestId/pause

  • Sets micStatus = OFF while keeping status = IN_PROGRESS
  • Calculates and stores remainingTime
  • Records pausedAt
  • Emits floorUpdate with event: PAUSED

Resume — POST /sittings/speaking-requests/:requestId/resume

  • Restores micStatus = ON
  • Clears pausedAt, restores speakingStart
  • Emits micOn with restored remainingTime

8. WebSocket — Floor Gateway

All floor events are emitted on the floor namespace. Clients must join a sitting room first:

// Subscribe
emit('joinSitting', '<sittingId>')
// → { status: 'joined', room: 'sitting_<id>', serverNow: '...' }

// Unsubscribe
emit('leaveSitting', '<sittingId>')

Events Received in the Sitting Room

EventWhen emittedPayload fields
floorUpdateQueue changes (request created/granted/denied/cancelled/paused)event, sittingId, request, serverNow
micOnMember's mic activatedsittingId, requestId, userId, timeLimit, remainingTime, speakingStart, serverNow, member
micOffMember's mic deactivated or pausedsittingId, requestId, userId, serverNow, event, remainingTime, member
agendaUpdateAgenda item state changedsittingId, agenda item data
voteUpdateVote session created/closedsittingId, sessionId, event, serverNow

micOff event values:

event valueMeaning
COMPLETEDSpeech concluded normally
PAUSED_FOR_POINT_OF_ORDERPaused because a Point of Order was raised
PAUSEDSpeaker paused speech manually

9. API Reference — Floor Control

MethodEndpointPermissionWho
POST/api/v1/sittings/:id/speaking-requestsfloor:request_speakMember submits request
GET/api/v1/sittings/:id/speaking-requestsfloor:list_requestsView ordered speaking queue
GET/api/v1/sittings/:id/current-speakersitting:readGet current IN_PROGRESS speaker
GET/api/v1/sittings/:id/active-votevote:readGet active vote (SO 81 check)
POST/api/v1/sittings/speaking-requests/:id/grantfloor:grant_speakSpeaker grants request
POST/api/v1/sittings/speaking-requests/:id/denyfloor:deny_speakSpeaker denies request
POST/api/v1/sittings/speaking-requests/:id/mic-onfloor:mic_controlActivate microphone
POST/api/v1/sittings/speaking-requests/:id/mic-offfloor:mic_controlMute microphone
POST/api/v1/sittings/speaking-requests/:id/pausefloor:mic_controlPause speech, freeze timer
POST/api/v1/sittings/speaking-requests/:id/resumefloor:mic_controlResume paused speech
POST/api/v1/sittings/speaking-requests/:id/cancelfloor:request_speakMember cancels own request

10. Full Floor Flow Example

1. Clerk creates sitting (SCHEDULED)
2. Speaker starts sitting → status = IN_PROGRESS

3. Member A POSTs /speaking-requests { requestType: "REGULAR" }
→ queuePosition = 1, status = PENDING
→ WebSocket: floorUpdate { event: "REQUEST_CREATED" }

4. Member B POSTs /speaking-requests { requestType: "REGULAR" }
→ queuePosition = 2, status = PENDING

5. Member C POSTs /speaking-requests { requestType: "POINT_OF_ORDER" }
→ queuePosition = 3, but sorts to position 0 in queue response

6. Speaker GETs /speaking-requests
→ [ Member C (POO, pos 0), Member A (pos 1), Member B (pos 2) ]

7. Speaker POSTs /speaking-requests/:A_id/grant
→ status = GRANTED, timeLimit = 1200 (20 min, regular member)
→ WebSocket: floorUpdate { event: "REQUEST_GRANTED" }

8. Speaker POSTs /speaking-requests/:A_id/mic-on
→ status = IN_PROGRESS, micStatus = ON, speakingStart = now
→ WebSocket: micOn { timeLimit: 1200, remainingTime: 1200, speakingStart, serverNow }

9. Member C (POO) POSTs /speaking-requests/:C_id/mic-on
→ Member A auto-paused: micOff { event: "PAUSED_FOR_POINT_OF_ORDER" }
→ Member C: micOn (no timer)

10. Speaker POSTs /speaking-requests/:C_id/mic-off
→ WebSocket: micOff { event: "COMPLETED" }

11. Speaker POSTs /speaking-requests/:A_id/resume
→ Member A resumes with remaining time preserved
→ WebSocket: micOn { remainingTime: <preserved> }

12. Speaker POSTs /speaking-requests/:A_id/mic-off
→ status = COMPLETED, speakingEnd recorded
→ WebSocket: micOff { event: "COMPLETED" }

13. Speaker POSTs /sittings/:id/complete → COMPLETED