Lesson 182: Cert Rehearsal Attendance Receipt and mock_audit_log Bootstrap from ICS Completion (2026)
Direct answer: Lesson 181 put ICS holds on the calendar. Lesson 182 closes the loop: when a hold completes, you write an attendance receipt (cert_rehearsal_attendance) keyed to the ICS UID, then bootstrap a draft mock_audit_log row only after receipt validation—so Lesson 173 recurrence histograms count real tabletops, not phantom invites.

Why this matters now (Q4 2026 partner readbacks)
Q4 2026 year-end governance readbacks shifted from “did you schedule a mock audit?” to “prove the tabletop ran”:
- Empty
mock_audit_logwith full calendars is a yellow on partner risk letters. - Lesson 173
deficiency_recurrence_12wtreats undated logs as noise—calendar-only teams look “stable” while never rehearsing. - Lesson 174 signer fatigue heat-maps assume ack traffic during real cert windows tied to completed rehearsals.
- Lesson 180 red-team runs must attach to a
mock_audit_log_idthat exists—not a Slack thread.
Attendance receipts are the mechanical bridge between operations (calendar) and analytics (trend board).
Lesson objectives
You will implement:
cert_rehearsal_attendancetable bound tocert_rehearsal_event.ics_uidforbid_mock_audit_log_without_attendancedatabase triggerbootstrap_mock_audit_log_from_attendancejob (draft → scored)- Panel roster validation against Lesson 172 four roles
ATTENDANCE_RECEIPT.jsonexport underrelease-evidence/05-operations/- Publish-gate extension:
mock_audit_log_orphanblock reason
Prerequisites
- Lesson 181 — ICS export,
cert_window_calendar,cert_rehearsal_event - Lesson 172 — scoring rubric, panel roles, 90-minute duration
- Lesson 173 — consumes dated
mock_audit_logrows - Lesson 171 — publish pipeline; attendance does not bypass tuple drift gates
Schema: cert_rehearsal_attendance
CREATE TABLE cert_rehearsal_attendance (
attendance_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cert_window_id TEXT NOT NULL REFERENCES cert_window_calendar(cert_window_id),
ics_uid TEXT NOT NULL UNIQUE,
rehearsal_phase TEXT NOT NULL CHECK (rehearsal_phase IN ('t_minus_14', 't_minus_3')),
scheduled_start_utc TIMESTAMPTZ NOT NULL,
actual_start_utc TIMESTAMPTZ,
actual_end_utc TIMESTAMPTZ,
duration_minutes INT GENERATED ALWAYS AS (
EXTRACT(EPOCH FROM (actual_end_utc - actual_start_utc)) / 60
) STORED,
scribe_signer_id TEXT NOT NULL,
panel_present_json JSONB NOT NULL,
attendance_status TEXT NOT NULL DEFAULT 'scheduled'
CHECK (attendance_status IN ('scheduled','completed','cancelled','no_show')),
receipt_sha256 CHAR(64),
created_at_utc TIMESTAMPTZ NOT NULL DEFAULT now()
);
Rules:
ics_uidmust exist incert_rehearsal_event(Lesson 181).panel_present_jsonmust list all four Lesson 172 roles with non-emptysigner_id.duration_minutesmust be ≥ 75 and ≤ 120 forcompleted(90-minute tabletop slack).
Trigger: forbid orphan mock_audit_log
CREATE OR REPLACE FUNCTION forbid_mock_audit_log_without_attendance()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.attendance_id IS NULL THEN
RAISE EXCEPTION 'mock_audit_log requires attendance_id';
END IF;
IF NOT EXISTS (
SELECT 1 FROM cert_rehearsal_attendance a
WHERE a.attendance_id = NEW.attendance_id
AND a.attendance_status = 'completed'
) THEN
RAISE EXCEPTION 'attendance must be completed before mock_audit_log insert';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_mock_audit_log_attendance
BEFORE INSERT ON mock_audit_log
FOR EACH ROW EXECUTE FUNCTION forbid_mock_audit_log_without_attendance();
This enforces the cultural rule: no scoring row without a completed rehearsal receipt.
Bootstrap job sketch
Run after scribe closes the tabletop (within 15 minutes):
def bootstrap_mock_audit_log(attendance_id: str) -> str:
att = load_attendance(attendance_id)
assert att["attendance_status"] == "completed"
assert att["duration_minutes"] >= 75
rubric = load_json("governance/mock_audit_rubric_v1.json")
log_id = insert_mock_audit_log(
attendance_id=attendance_id,
cert_window_id=att["cert_window_id"],
rubric_version=rubric["rubric_version"],
status="draft",
pass_bit=None, # computed after scoring
)
write_receipt(att, log_id)
return log_id
Draft vs scored: Panel scores in Lesson 172 UI or spreadsheet import flips status from draft → scored and sets computed pass bit—never manual toggle.
ATTENDANCE_RECEIPT.json
Commit per completed rehearsal:
{
"attendance_id": "8f2c-…",
"cert_window_id": "q1_2027_meta_holiday",
"ics_uid": "[email protected]",
"rehearsal_phase": "t_minus_14",
"scheduled_start_utc": "2027-01-08T14:00:00Z",
"actual_start_utc": "2027-01-08T14:03:00Z",
"actual_end_utc": "2027-01-08T15:28:00Z",
"panel_present": [
{"role": "scribe", "signer_id": "eng.alice"},
{"role": "leadership_voice", "signer_id": "ceo.bob"},
{"role": "partner_voice", "signer_id": "partner.liaison"},
{"role": "engineering_voice", "signer_id": "eng.carol"}
],
"mock_audit_log_id": "log-…",
"receipt_sha256": "…"
}
Store under release-evidence/05-operations/rehearsal-attendance/.
Six-step completion procedure
| Step | Owner | Action | SLA |
|---|---|---|---|
| 1 | Scribe | Mark ICS event accepted; record actual_start_utc |
T+0 |
| 2 | Scribe | Verify four panel roles present | T+5 min |
| 3 | Scribe | Insert cert_rehearsal_attendance → completed |
T+10 min |
| 4 | Engineering | Run bootstrap job → mock_audit_log draft |
T+15 min |
| 5 | Panel | Score tabletop → scored + computed pass |
T+24 h |
| 6 | Governance owner | Export receipt JSON + SHA-256 to evidence folder | T+48 h |
Publish-gate extension
Add to Lesson 171 block_reason allow-list:
mock_audit_log_orphan— publish blocked while anymock_audit_logrow lacksattendance_idor attendance notcompleted.
Integration with Lesson 173 trend board
After bootstrap + scoring:
- Nightly refresh
deficiency_recurrence_12wincludes onlymock_audit_log.status = 'scored'. cert_window_idon log rows powers cross-window histograms.- Friday Block 6 compares sprint hardening budget to closed deficiencies from scored logs only.
Common mistakes
- Inserting
mock_audit_logbefore attendancecompleted. - Same human listed for two panel roles (Lesson 172 allows distinct voices—receipt should too).
- Using calendar “maybe” responses as attendance proof.
- Skipping
receipt_sha256on export (partner cannot reproduce). - Marking
no_showbut still inserting deficiencies “from memory.” - Bootstrap job creating duplicate logs for same
ics_uid(enforceUNIQUEon attendance → one log).
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Trigger rejects insert | Attendance still scheduled |
Complete step 3 first |
| Duration check fails | End time typo | Reconcile with scribe notes |
| Missing role in JSON | Panel no-show | Reschedule; do not fake receipt |
Duplicate ics_uid |
Re-exported ICS with new UID | Lesson 181 UID stability rule |
| Trend board empty | Logs stuck in draft |
Run scoring step 5 |
Verification checklist
- [ ] Completed rehearsal has
cert_rehearsal_attendancerow - [ ]
mock_audit_log.attendance_idFK populated - [ ] Trigger blocks insert without completed attendance
- [ ]
ATTENDANCE_RECEIPT.jsonin evidence folder with SHA-256 - [ ] Lesson 173 view shows new window after scored log
- [ ] Publish gate fires on deliberate orphan log test
Mini exercise (25 minutes)
- Pick one Lesson 181 ICS UID from your calendar export.
- Write a fake-but-valid attendance JSON (four roles, 85-minute duration).
- Draft SQL insert statements for attendance + draft log.
- List one
block_reasonthat still applies from Lesson 171.
Continuity
- Lesson 181 — ICS source; do not change UID scheme here.
- Lesson 172 — scoring rules unchanged; this lesson only gates existence of log rows.
- Lesson 173 — primary consumer of scored logs.
- Lesson 180 — attach red-team packet IDs to
mock_audit_log_idfrom this bootstrap. - Next: Lesson 183 — export deficiencies to sprint allocator feed.
- Blog: Q3 2026 mock audit tabletop — panel format reference.
FAQ
Can we backfill 2026 Q3 logs without receipts?
One-time migration with attendance_status = 'completed' and archived evidence only—do not make it the default path.
What if only three panelists attend?
Mark cancelled, reschedule. Partial panels fail Lesson 172 muscle-memory goal.
Does Google Calendar auto-write attendance?
No. Scribe (or delegate) must insert SQL/JSON within 15 minutes—calendar is schedule, not proof.
ICS declined—still bootstrap?
No. no_show blocks bootstrap by design.
Scheduled rehearsals are intentions. Attendance receipts plus gated mock_audit_log bootstrap are proof—and proof is what 2026 partner readbacks count.