# Runbook — Yemaya study workspace: tenant export and closure

**Checklist:** YSD-22058 (`tenant closure`) · drill kind `tenant-closure` ·
YSD-17106 **Decision owner role:** the service owner for `svc-study-workspace`
**Machinery:** `libs/yemaya/study-workspace/src/continuity/disaster-recovery.ts`

This had no document and no nearest guidance — nothing at all. It is also two
scenarios rather than one: `tenant-data-export` and `tenant-closure` share the
`tenant-closure` drill kind, because **the export is the first half of the
closure and a drill of the closure necessarily performs it.**

That sharing is the safety property. A closure that has not exported first is an
unrecoverable deletion performed on somebody else's material.

## Order, and why it is not negotiable

1. **Export first, and verify the export before deleting anything.**
2. **Then close.**

⚠️ Nothing in a closure is reversible, and the tenant is by definition no longer
around to tell you something is missing. A verified export is the only thing
standing between a closure and a data-loss incident that surfaces months later.

## What the export must contain

The workspace's own answer to "what did this tenant have" is the same one the
backup uses: every study table, enumerated from the catalog rather than from a
list somebody maintains, filtered to the tenant. Plus the tenant's objects in
`oshun-study`, where the key grammar is tenant-scoped (YSD-3021), so the tenant
prefix is a real boundary and not a filter applied afterwards.

**What the export must NOT quietly include:** another tenant's material
reachable through a shared work. If a source is shared, the tenant's
_annotations_ are theirs and the _source_ is not. Exporting the source because
it was reachable hands one tenant another's licensed material.

## The steps

| #   | Action                                                                                                          | Owner role                | Expected |
| --- | --------------------------------------------------------------------------------------------------------------- | ------------------------- | -------- |
| 1   | Produce the export, tenant-scoped at the key grammar and the table filter                                       | service owner             | —        |
| 2   | **Verify it** — counts per table against the live tenant, and a spot read through the store rather than raw SQL | service owner             | —        |
| 3   | Deliver it, and get acknowledgement that it arrived and opens                                                   | service owner             | —        |
| 4   | Only then close: delete tenant rows and objects, leaving tombstones                                             | **service owner decides** | —        |
| 5   | Confirm shared sources survived, and that no other tenant lost access                                           | service owner             | —        |

Step 5 is the one a closure gets wrong. A deletion that follows references
rather than tenancy takes a shared work down with the tenant that referenced it.

## Communication

| Audience                                                 | Within        |
| -------------------------------------------------------- | ------------- |
| The tenant, with the export and what closure will remove | before step 4 |
| The rights owner, where licensed material was in scope   | before step 4 |

## Done means

- The export was verified and acknowledged **before** anything was deleted.
- Tenant rows and objects are gone; tombstones remain.
- No other tenant's access changed.
- Recorded in the procedure exercise register.

## The objective, and why there is not one

**This procedure states no duration objective, and that is a decision rather
than an omission.** `evaluateDrill` refuses a drill record without one, so the
consequence is exact: **tenant closure cannot be drilled today**, and the reason
is not that nobody has written a number down.

Two things stand in the way, and neither is fixed by choosing a duration.

**Step 3 waits on somebody outside.** The tenant has to acknowledge that the
export arrived and opens, and that acknowledgement is not ours to schedule. A
whole-procedure objective would be a promise about how fast a departing customer
answers their email, and the way to meet such an objective is to stop waiting
for the acknowledgement — which is the one thing the order of operations above
exists to prevent.

**Steps 1 and 2 have no machinery to measure.** There is no export-specific code
in the service; nothing has ever produced a tenant-scoped export, so there is no
floor under a number the way `scripts/time-backup-restore.ts` and
`scripts/time-deletion.ts` put one under theirs. A duration nobody has measured
is the fabricated measurement this programme keeps refusing.

**What would change this**, in order: build the export, measure steps 1, 2 and 5
against a real deployment, and state an objective over those three — leaving
steps 3 and 4 outside it exactly as `yemaya-study-provider-change.md` leaves its
rights decision outside its containment objective. The Expected column above
stays blank until then.

## Known shortfall

**Nothing exercises this — zero mechanism runs, and no export-specific machinery
was found in the service.** The expected durations above are deliberately left
blank rather than guessed, for the reasons in the section directly above. Read
this as the order of operations that must hold, not as a rehearsed procedure.
