Lesson 185: Mock Audit Rubric JSON Semver Drift Block on Calendar Re-Export (2026)
Direct answer: Lesson 181 exports ICS holds from mock_audit_rubric_v1.json. Lesson 185 adds a semver gate: if the committed rubric JSON changes dimensions, weights, or failure-mode tags without bumping rubric_version, the calendar re-export job refuses to run and publish stays blocked until holds are regenerated with matching version pins.

Why this matters now (2026 rubric drift on booked holds)
Teams already hit this in Q4 2026 planning:
- Panel books January T-14 rehearsal from ICS exported in October.
- Engineering edits
mock_audit_rubric_v1.json(adds dimension, reweights) without semver bump. - Calendar UIDs look unchanged—scribe runs tabletop with new rubric, attendance receipt still references old event description weights.
- Lesson 172 scoring rejects rows; Lesson 183 CSV export empty; leadership blames “calendar bug.”
The failure is version skew, not scheduling. This lesson makes skew mechanical.
Lesson objectives
You will implement:
mock_audit_rubric_registrywith immutablerubric_versionrowscert_window_calendar.rubric_versionpin on every holdforbid_calendar_reexport_without_semver_bumpjob guardrubric_semver_drift_openpublish-gate extension- Re-export procedure paired with Lesson 177 dictionary migration phases
Prerequisites
- Lesson 181 — ICS export + stable
ics_uid - Lesson 177 — dictionary semver migration pattern (dual-write discipline)
- Lesson 184 — clear room conflicts before re-export
- Lesson 172 — scoring uses rubric dimensions from registry
mock_audit_rubric_registry table
CREATE TABLE mock_audit_rubric_registry (
rubric_version TEXT PRIMARY KEY,
rubric_sha256 CHAR(64) NOT NULL,
effective_at_utc TIMESTAMPTZ NOT NULL,
changelog TEXT NOT NULL,
retired_at_utc TIMESTAMPTZ
);
ALTER TABLE cert_window_calendar
ADD COLUMN rubric_version TEXT NOT NULL DEFAULT '1.0.0'
REFERENCES mock_audit_rubric_registry (rubric_version);
On every commit to governance/mock_audit_rubric_v1.json:
- Compute SHA-256 of normalized JSON (sorted keys, LF endings).
- If hash differs from active registry row → require new
rubric_version(semver). - Insert registry row before any calendar re-export.
Semver rules (rubric JSON)
| Change class | Bump | Example |
|---|---|---|
Typo in changelog only |
patch 1.0.0 → 1.0.1 |
README note |
Add dimension, reweight, new failure_mode |
minor 1.0.1 → 1.1.0 |
New Lesson 173 column |
Remove dimension or change pass_rule |
major 1.1.0 → 2.0.0 |
Panel contract break |
Forbidden: edit weights in place while rubric_version stays 1.0.0.
Calendar re-export guard
def reexport_calendar(cert_window_id: str) -> None:
active = load_committed_rubric_json()
registry = query_one(
"SELECT rubric_version, rubric_sha256 FROM mock_audit_rubric_registry "
"WHERE retired_at_utc IS NULL ORDER BY effective_at_utc DESC LIMIT 1"
)
if sha256(active) != registry.rubric_sha256:
raise RubricSemverDrift(
"Committed rubric hash differs from registry. Bump rubric_version first."
)
pinned = query(
"SELECT DISTINCT rubric_version FROM cert_window_calendar WHERE cert_window_id = %s",
[cert_window_id],
)
if len(pinned) != 1 or pinned[0] != registry.rubric_version:
raise RubricSemverDrift(
"Calendar holds pin a different rubric_version. Re-export required."
)
run_lesson_181_exporter(cert_window_id, rubric_version=registry.rubric_version)
Publish-gate extension
Add to Lesson 171 allow-list:
rubric_semver_drift_open— active holds’rubric_version≠ registry head, or committed JSON hash mismatch.
Blocks publish until:
- Registry row inserted for new semver.
- Lesson 181 re-export completes with same ICS UIDs (Lesson 181 rule).
GOVERNANCE_REHEARSAL_README.mdchangelog line added.
Pairing with Lesson 177 dictionary migration
Run rubric semver bumps in the same change window as dictionary minor migrations when both touch panel scoring:
| Phase | Dictionary (177) | Rubric (185) |
|---|---|---|
| prepare | Freeze exports | Freeze calendar re-export |
| dual_write | Dual column names | Optional: hold description lists both versions |
| cutover | Switch exporters | Re-export ICS with new rubric_version |
| rollback | Revert dictionary semver | Retire rubric registry row; re-export prior semver |
Never cutover dictionary while calendar still pins old rubric_version.
Re-export procedure (after semver bump)
| Step | Owner | Action |
|---|---|---|
| 1 | Governance owner | Bump rubric_version + insert registry row |
| 2 | Engineering | Verify Lesson 184 conflicts = 0 |
| 3 | Scribe | Run guarded re-export job |
| 4 | Panel lead | Confirm ICS descriptions show new version in subject line |
| 5 | Governance owner | Clear rubric_semver_drift_open gate |
ICS subject template:
[mock-audit v1.1.0] Q1 2027 Meta T-14 Rehearsal
Evidence artifact
release-evidence/05-operations/RUBRIC_SEMVER_YYYYMMDD.json:
{
"schema": "rubric_semver_export_v1",
"from_version": "1.0.0",
"to_version": "1.1.0",
"rubric_sha256": "…",
"cert_window_id": "q1_2027_meta",
"ics_uid_count": 4,
"reexport_at_utc": "2026-10-12T16:00:00Z"
}
Common mistakes
- Hand-editing ICS descriptions without registry row.
- Minor weight tweak without semver—breaks Lesson 172 muscle memory.
- New
ics_uidon re-export when only semver bumped—breaks Lesson 182 receipts. - Running re-export while Lesson 184 conflicts open.
- Dictionary migration cutover without rubric re-export.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Job raises RubricSemverDrift |
JSON changed, version not bumped | Insert registry + bump semver |
| Gate stuck after re-export | Holds still old version column | UPDATE pins or full re-export |
| Panel sees mixed weights | Description not updated | Re-export with version in subject |
| Scoring allow-list rejects tags | New failure_mode without minor bump | Bump rubric + re-export |
Verification checklist
- [ ] Hash change without version bump fails CI
- [ ] Registry row required before re-export succeeds
- [ ] All holds share one
rubric_versionper window - [ ] Publish gate fires on deliberate drift test
- [ ] ICS UIDs stable across semver re-export
Mini exercise (20 minutes)
- Copy
mock_audit_rubric_v1.json; change oneweight_percentby 1. - Write expected CI failure message.
- Bump minor semver; draft registry INSERT.
- List one Lesson 177 phase that must align.
Continuity
- Lesson 181 — exporter implementation; UID stability rules unchanged.
- Lesson 177 — dictionary semver sibling discipline.
- Lesson 184 — run before re-export.
- Next: Lesson 186 — regional panel roster across time zones.
- Guide: Unity mock-audit rubric preflight chapter pairs Q3 intake tabletop.
FAQ
Can we use calendar sequence numbers instead of semver?
No. Panel and SQL tooling key off rubric_version string in registry.
Patch bump for typo in dimension name?
Yes if scoring allow-list unchanged; still re-export ICS so descriptions match.
Major bump mid-window?
Allowed with leadership ack; reschedule T-3 if panel already trained on prior weights.
Does this replace git tags?
No—registry is operational truth for gates; git tags are optional metadata.
Booked rehearsals are contracts. If the rubric changes, the version must change—and the calendar must say so before anyone walks into the room.