Work

Change Advisory Board (CAB)

A CABMeeting is a scheduled review of a batch of changes by a designated group of stakeholders. CAB meetings exist when you want a human gate with a…

Last updated

Overview

A CABMeeting is a scheduled review of a batch of changes by a designated group of stakeholders. CAB meetings exist when you want a human gate with a quorum — not a single approver clicking through a queue, but a room (physical or virtual) where several people walk an agenda, debate risk, and record a collective decision per change.

The CAB lives at /ws/{slug}/changes/cab. The list shows upcoming and past meetings; each meeting has its own detail page with agenda, attendees, chair, minutes, and the per-change decision log.

Why it exists

For high-risk, cross-team, or regulated changes, a single approver isn't enough. ITIL, SOX, and most enterprise change-management policies require a board review. Vigilo models the board as a first-class object so the board's decisions live next to the changes they decided, with the same audit trail and webhook surface as individual approvals.

CAB-as-gate ties the two together: an ApprovalPolicy stage with kind="cab" produces an ApprovalStep with stage_type='cab' that doesn't resolve until the relevant CABMeeting.record_decision() fires. The board's decision becomes the change's approval.

Key concepts

  • CABMeeting — Workspace-scoped meeting record. Has title, scheduled_at, duration_minutes (default 60), status, a chair (FK to UserProfile), attendees (M2M to UserProfile), and changes (M2M to ChangeRequest — the agenda).
  • status — One of scheduled, in_progress, completed, cancelled. The status drives which actions appear on the toolbar.
  • agenda — The changes M2M is the agenda. A change can be on multiple CABs (e.g. pre-CAB then formal CAB). Adding a change with a pending CAB-stage ApprovalStep automatically binds the step to this meeting via cab_meeting.
  • decisions — Free-form JSON dict keyed by change number, e.g. {"CHG-00012": {"decision": "approved", "notes": "Hold for retest"}}. Updated by the Record decision action and used to drive ApprovalStep.status for CAB-gated steps.
  • CAB stage type — Inside an ApprovalPolicy, a stage with kind="cab" declares "this gate is a CAB decision". When the change submits, an ApprovalStep with stage_type='cab' is created; until the change appears on a CAB agenda and the meeting records a decision, the step stays pending and SLA accrues.
  • Workspace isolation — CAB meetings, their attendees, their changes, and their decisions all carry workspace FKs. RLS guarantees a CAB from workspace A is invisible to workspace B (test_cab_isolation.py proves it; T3.2).

Common workflows

Scheduling a CAB

  1. Navigate to Changes → CAB → New meeting.
  2. Fill in title, scheduled_at (date + time), duration_minutes, chair, and the initial attendee list.
  3. Save. The meeting lands in scheduled and a cab.scheduled event fires via dispatch_event, so chat integrations can post a calendar invite.

Building the agenda

  1. From the CAB detail page, click Add changes. The picker shows changes with status='review' and at least one pending ApprovalStep of stage_type='cab' not yet bound to another meeting.
  2. Pick the changes; save. Each change's CAB step is now bound to this meeting via cab_meeting.
  3. Optionally reorder the agenda by dragging items in the list — the order is preserved in the JSON payload that drives the meeting view.

Running the meeting

  1. On the day, the chair clicks Start meeting. Status moves to in_progress.
  2. Walk the agenda. For each change, the meeting view shows the change preview, the discussion area (minutes field appended to as a single textarea), and an inline Record decision widget.
  3. Click Approve or Reject on each row, optionally with notes. The action calls services.record_decision(step, ...) which sets the bound ApprovalStep.status and decision_at, advances the change FSM if this was the last step, and writes {change_number: {decision, notes}} into the meeting's decisions field.
  4. When every agenda item has a decision, click Complete meeting. Status moves to completed. A cab.completed event fires.

Per-meeting maintenance (PR2)

  • Reschedule — Pick a new scheduled_at. Available while status is scheduled. Attendees and agenda are preserved.
  • Cancel — Moves status to cancelled. CAB-bound ApprovalStep rows are released (set back to unbound) so the changes can be moved to another meeting without spawning new steps.
  • Delete — Removes the meeting record entirely. Refuses if status is in_progress or completed (preserve the audit trail). Available to admins only.

Permissions

  • Viewers can see CAB meetings on the calendar but not attend or vote.
  • Attendees (anyone in the meeting's attendees M2M) can read the agenda, post minutes, and — if they also hold the changes.approve permission — record decisions.
  • Chairs can start, complete, reschedule, and cancel their own meetings.
  • Admins / owners can delete meetings and reassign chair.

Troubleshooting

The change doesn't appear in the "Add changes" picker — Either it doesn't have a CAB-stage ApprovalStep (check the change's policy), the step is already bound to another meeting, or the change isn't in status='review'. Promote it to review first or re-issue the approval policy.

"Record decision" is greyed out — The change has been removed from this meeting's agenda, or the bound ApprovalStep has already been decided elsewhere (e.g. another meeting). Refresh; the row should disappear.

The CAB completed but the change is still in review — The change had additional non-CAB ApprovalStep rows that haven't been approved. Check the Approvals tab on the change for the full chain.

I can't see another workspace's CAB I was invited to — You can't. CAB meetings are workspace-scoped. You need to be a member of the workspace the CAB belongs to (WorkspaceMembership with is_active=True).

Related