Lesson 195: Partner-Sanitized Rehearsal Score Letter Export Without Raw mock_audit_log Rows (2026)
Direct answer: Lesson 193 exports rehearsal_completion_v1.json. Lesson 195 projects that JSON—and dimension rollups only from Lesson 172 scoring—into a partner-safe PARTNER_SCORE_LETTER.md. Raw mock_audit_log rows, signer emails, internal ticket IDs, and panel notes never leave the redaction boundary. Publish blocks on partner_letter_draft_open until legal approves the template and the export passes an allow-list scan.

Why this matters now (Q4 2026 partner readbacks)
Q4 2026 partner templates changed again:
- Attachments with raw
mock_audit_logCSV and signer email columns now trigger yellow-flag on readback—not because scores were wrong, but because PII crossed the partner boundary. - Partners want dimension summaries and phase completion proof from Lesson 193, not SQL dumps.
- Lesson 194 intake folders explicitly exclude raw logs; this lesson is the sanitized letter that belongs beside
rehearsal_completion_v1.json. - Lesson 170 FAQ tone rules apply to every sentence partners quote back to executives.
Lesson objectives
You will implement:
- View
partner_score_letter_redacted_v1 - Allow-listed column contract (no PII, no raw log IDs)
- Markdown template
PARTNER_SCORE_LETTER.md - Export job
export_partner_score_letter_v1 - Publish gate
partner_letter_draft_open - Receipt
PARTNER_SCORE_LETTER_RECEIPT.json
Prerequisites
- Lesson 193 —
rehearsal_completion_v1.json,rehearsal_completion_incompleteclear - Lesson 172 — seven-dimension rubric labels and weights
- Lesson 170 — FAQ-bound readback tone for letter prose
- Lesson 194 — optional; letter ships inside intake folder when ready
Allow-listed export fields
| Field | Source | Partner-safe |
|---|---|---|
cert_window_id |
rollup JSON | Yes |
completion_score_percent |
Lesson 193 | Yes |
phase (t_minus_14, t_minus_3) |
rollup JSON | Yes |
pass_bit per phase |
rollup JSON | Yes |
weighted_score per phase |
rollup JSON | Yes (aggregate only) |
dimension_id (1–7) |
rubric rollup | Yes |
dimension_label |
rubric | Yes |
dimension_score_percent |
aggregated | Yes |
structural_red_flag (bool) |
rubric | Yes |
rubric_version |
rollup JSON | Yes |
Forbidden in partner lane (fail export):
signer_email,panelist_email,scribe_emailmock_audit_log_id,attendance_id,internal_ticket_id- Free-text
panel_notes,deficiency_detail, raw CSV paths - Any column matching
%_emailor%_hashexcept publicbundle_sha256on manifests
partner_score_letter_redacted_v1 (view)
CREATE VIEW partner_score_letter_redacted_v1 AS
SELECT
r.cert_window_id,
r.completion_score_percent,
p.rehearsal_phase,
p.pass_bit,
p.weighted_score,
p.rubric_version,
d.dimension_id,
d.dimension_label,
d.dimension_score_percent,
d.structural_red_flag
FROM rehearsal_completion_export_staging r
JOIN rehearsal_completion_phases_staging p
ON p.cert_window_id = r.cert_window_id
JOIN mock_audit_dimension_rollup_staging d
ON d.cert_window_id = r.cert_window_id
AND d.rehearsal_phase = p.rehearsal_phase
WHERE r.export_schema = 'rehearsal_completion_v1';
Staging rule: ETL loads from rehearsal_completion_v1.json only—never SELECT * FROM mock_audit_log.
PARTNER_SCORE_LETTER.md template
# Rehearsal completion summary — {{cert_window_id}}
**Planning window:** Q1 2027 intake rehearsal
**Exported:** {{exported_at_utc}}
**Completion score:** {{completion_score_percent}}%
## Phase results
| Phase | Pass | Weighted score | Rubric |
|-------|------|----------------|--------|
| T-14 | {{t14_pass}} | {{t14_score}} | {{t14_rubric}} |
| T-3 | {{t3_pass}} | {{t3_score}} | {{t3_rubric}} |
## Dimension summary (no raw panel notes)
| # | Dimension | Score % | Structural red |
|---|-----------|---------|----------------|
{{#each dimensions}}
| {{dimension_id}} | {{dimension_label}} | {{dimension_score_percent}} | {{structural_red_flag}} |
{{/each}}
## Partner-facing statement
Both required rehearsal phases completed with scored logs. Dimension summaries above are aggregated; raw audit rows are not included per 2026 partner readback policy.
**Questions:** Reply via your governance channel referencing `cert_window_id` only.
Render with FAQ-bound tone checks from Lesson 170 (no blame language, no internal codenames).
Export job
FORBIDDEN_SUBSTRINGS = ("@", "mock_audit_log_id", "signer_", "panel_notes")
def export_partner_score_letter(cert_window_id: str, out_dir: Path) -> Path:
rows = query_view("partner_score_letter_redacted_v1", cert_window_id)
md = render_partner_letter_template(rows)
for bad in FORBIDDEN_SUBSTRINGS:
if bad in md:
raise RedactionError(f"forbidden token in letter: {bad}")
path = out_dir / "PARTNER_SCORE_LETTER.md"
path.write_text(md, encoding="utf-8")
receipt = {
"schema": "partner_score_letter_receipt_v1",
"cert_window_id": cert_window_id,
"sha256": sha256_file(path),
"allow_list_version": "2026-q4-partner-v1",
"raw_log_rows_included": False,
}
write_json(out_dir / "PARTNER_SCORE_LETTER_RECEIPT.json", receipt)
return path
Publish gate partner_letter_draft_open
Pipeline sets partner_letter_draft_open when any of:
| Condition | Block reason |
|---|---|
| Legal has not approved template version | letter_template_unapproved |
Allow-list scan finds @ in output |
pii_email_detected |
rehearsal_completion_incomplete still open |
rollup_not_passing |
| Export references raw CSV path string | raw_csv_reference |
raw_log_rows_included true in receipt |
raw_logs_flagged |
Clear gate only after legal sign-off row in partner_letter_template_approval and successful allow-list scan.
Ninety-second preflight
- [ ]
rehearsal_completion_v1.jsonshows"pass": true - [ ] View query returns zero email columns
- [ ]
PARTNER_SCORE_LETTER.mdhas no@and nomock_audit_log_id - [ ] Seven dimension rows present (or documented waiver)
- [ ]
PARTNER_SCORE_LETTER_RECEIPT.jsonshowsraw_log_rows_included: false - [ ]
partner_letter_draft_openclears on dry-run publish
Troubleshooting
| Symptom | Fix |
|---|---|
| Partner asks for “the CSV” | Resend letter + rehearsal_completion_v1.json; cite 2026 redaction policy |
| Dimension scores missing | Refresh mock_audit_dimension_rollup_staging from scored logs inside warehouse only |
| Legal blocks template | Bump allow_list_version; re-approve FAQ tone |
| Yellow-flag on email in footer | Strip mailto links from template |
Mini exercise (25 minutes)
- Load fixture
rehearsal_completion_v1.jsonwith passing phases. - Run export; grep letter for
@(expect no matches). - Inject
signer_emailinto staging—confirm export raisesRedactionError. - Attach letter to Lesson 194 intake folder dry-run.
- Pin receipt JSON.
Continuity
- Lesson 193 — rollup source.
- Lesson 172 — dimension labels.
- Lesson 170 — prose tone.
- Lesson 194 — intake folder sibling (no raw logs).
- Next: Lesson 196 — dashboard slice fails closed when rollup incomplete.
FAQ
Can partners receive hashed signer IDs?
No—use cert_window_id and phase pass bits only unless counsel approves a new allow-list version.
Do we attach dimension deficiency tickets?
Not in the partner letter lane; internal tickets stay in ops tools.
What if T-3 pass_bit is false?
Letter still exports with honest fail summary; partner_letter_draft_open may remain until remediation—do not hide gaps.
How does this relate to Lesson 199?
Attestation bundles the letter receipt with Lessons 194–198 green gates.
Q4 2026 partners trust short, redacted letters backed by rehearsal_completion_v1.json—not spreadsheets that leak who sat in the panel and which internal ticket IDs were debated.