# Study & Deconstruction Workspace — Support Procedure (YSD-19125 / YSD-22058 / YSD-18163)

The procedure for answering somebody who is stuck, and for reading their
material only when answering them needs it.

This is the document YSD-22058's `support` procedure had none of. It is not an
incident runbook: an incident is something the platform did to people, and
`incident-runbooks.md` covers those. This is the ordinary bad day — a learner
cannot open their notes, an administrator thinks a lens is wrong, a restore left
something missing — where the platform is being _asked_, and the risk is not
that nobody responds but that responding turns into a stranger reading a
learner's work with nothing bounding how much or for how long.

**Why this document exists at all.** `operational-drills.ts` says it plainly:
the taxonomy of procedures this programme drilled was a taxonomy of things that
UNDO or REPAIR, and "support runs whenever somebody is stuck" was left out of
it. "We do it all the time" is a statement about frequency, not about whether
the documented procedure has ever been followed and measured.

---

## 1. The accountable role and the response objective

**Accountable role:** the workspace support rotation. Not "whoever notices" —
the drill this procedure is measured by asks that the reporter "was answered
inside the response objective by the accountable role, not by whoever happened
to notice", and an answer from a passer-by is the thing that check exists to
catch.

**Response objective: the reporter is answered within 24 hours of the case being
opened.** Borrowed, not invented: `MAX_MODERATION_RESPONSE_HOURS` in
`libs/yemaya/study-workspace/src/policies/institutional-controls.ts` is this
programme's own outer bound on how long something can sit before it stops
meaning anything to the person who raised it — "past that the report is an
archive entry, not a report". A support case is a report by the person affected;
the same bound applies for the same reason.

"Answered" means a person has read the case and said what happens next. It does
not mean resolved, and it does not mean an automatic acknowledgement.

---

## 2. Answer without reading their material first

Most cases do not need anybody's material. The two classes support reaches that
belong to nobody — `operational-record` (job, queue, pipeline and transcode
state, error-taxonomy entries) and `tenant-configuration` (adopted controls,
adapters, feature flags) — answer the great majority of cases on their own, and
`SUPPORT_ONLY_SCOPES` exists to make it possible to say afterwards that an
episode touched nothing of anyone's.

Work in this order, and stop as soon as the case is answered:

1. **Reproduce from the operational record.** Job state, retries, the error the
   pipeline recorded, the technical fingerprint of the source.
2. **Check the tenant's own configuration.** A lens that "gives the wrong
   answer" is very often a lens the institution's policy does not permit in that
   lane, and the refusal reached the learner as a message they did not read.
3. **Ask the reporter.** They are present. The narrowest way to find out which
   note is missing is to ask which note is missing.

Only when the answer genuinely requires opening a record does §3 apply.

---

## 3. Opening a bounded window

A support read is not a permission somebody holds; it is a window that is opened
for a case, names what it may reach, and ends. The mechanism is
`libs/yemaya/study-workspace/src/policies/support-access.ts`.

**Open it with `openSupportAccess`**, which refuses everything
`evaluateControlRequest` already refuses and then refuses more. State:

| field             | what it is                                             |
| ----------------- | ------------------------------------------------------ |
| `requesterId`     | you, by name. Not the rotation, not a service account. |
| `caseRef`         | the case. A read with no case has no reason to end.    |
| `purpose`         | one of the four the institution adopted.               |
| `requestedScopes` | the classes you expect to need — not "everything".     |
| `invitedScopes`   | the classes the reporter themself pointed at.          |
| `subjectUserId`   | whose material it is, whenever it is anybody's.        |
| `windowHours`     | how long you expect to need, inside the clause's cap.  |

**What each purpose reaches** is `SCOPE_JUSTIFYING_SUPPORT_PURPOSES`, and it is
not a formality — a class outside the purpose is refused at the grant and again
at every read:

- **`ticket-resolution`** — the reporter's own material, and the only purpose
  that reaches `learner-identity`, because "I cannot sign in" is the one
  question that needs to know who somebody is.
- **`data-recovery`** — the classes being recovered, including
  `source-material`. The only purpose that opens the media itself.
- **`incident-investigation`** and **`security-review`** — system state and the
  audit trail, and no learner material at all. Which records an incident
  implicated is a question the audit trail answers; opening them to find out is
  not investigating the exposure, it is repeating it. A security review that
  reads student work is not a security review.
- **`cohort-analytics` is reachable by nothing.** If you believe you need it,
  the case is about the records underneath it, and those are reachable.

**Material the reporter did not point at.** A class that belongs to somebody —
`learner-attempt`, `learner-identity`, `assessment-record`, `study-artifact` —
is readable only where the subject named it in the case, or where an institution
administrator approved reaching it. There is no third way in. If you find
yourself needing a record nobody offered, go back to the reporter or go to the
institution; do not widen the window.

**Take the narrowest window you can work in.** The clause's cap is the
institution's outer bound, not a target: "a support window longer than a working
week is standing access with a date" on it.

---

## 4. Inside the window

**Every read goes through `evaluateSupportRead` and every result is written
down, allowed or refused.** A refusal nobody recorded is indistinguishable from
a request nobody made, and the pattern an investigation looks for is repeated
refusals. The record it returns carries the requester, the class, whose material
it was, the record reached and the case — which is what the adopted permission
`support-access:<purpose>` cannot carry, since that names what was claimed
rather than what was reached.

**The decision is taken again at every read**, and any of these can have changed
since the window opened:

- the institution withdrew support access, or withdrew this purpose;
- the institution turned on per-read approval;
- the institution shortened its window, which shortens grants already open — a
  narrowing binds, a widening does not extend anything anybody already holds;
- the window simply ran out.

If a read is refused, read the reason before retrying. `subject-mismatch` and
`uninvited-scope` in particular are not paperwork: they mean the record you
reached for belongs to somebody this case is not about, or to somebody who did
not offer it.

**The minor rule applies to the material, not to the reporter.** A window over
the operational record is not refused because the person who raised the case is
fifteen; a read of their own work is. State `subjectIsMinor` truthfully — the
gate is built so that the honest answer is never the one that costs you the
window you actually need.

---

## 5. Closing the case

Three things close a case, and the drill measures the second and third.

**Revoke the window when the case is answered, not when it expires.**
`revokeSupportAccess` brings the end forward, and every later read is refused
`grant-revoked` — a revocation the reads cannot see is a note somebody wrote
about an access that carried on. The review measures how long the access
outlived its case against the grant's own end, so a revocation is the only thing
that shortens it: narrowing the institution's clause afterwards does not
retroactively close a window that was open all week. A revocation dated after
the window had already lapsed withdraws nothing, and the review says so.

**Write the resolution where the next occurrence will find it.** The support
drill's own words: "the resolution was written where the next occurrence will
find it, rather than ending in one person's memory". A recurring cause belongs
in the error taxonomy or in this document; a one-off belongs in the case.

**Tell the subject their material was read.** This is a duty, not an option, and
it has no clause to switch it off. The institution's moderation clause already
states the principle for the smaller case — an institution may "delay notice for
a safety investigation; it may not decide never to give" it, "because a removal
nobody is told about is a removal nobody can contest" — and that clause governs
the _same_ platform staff reading the _same_ learner's _reported_ material. A
support read is wider and lasts longer; it cannot be the silent one.

`disclosureOwed` computes it:

- Owed only where a class belonging to an identified person was actually opened.
  An episode that read the transcode queue owes nobody anything, and a notice
  about it would be noise that teaches people to ignore the ones that matter.
- Due at the earlier of the case closing and 24 hours after the last read, for
  `ticket-resolution` and `data-recovery` — cases the subject is a party to.
- May wait for the case to close for `incident-investigation` and
  `security-review`, and no longer than 168 hours after the last read if the
  case does not close. An institution that calls a week the outer bound on how
  long an access may stay open has already said what it thinks a week means.
- A notice sent _before_ the last read does not discharge it. A statement issued
  on Monday cannot describe Wednesday's read.
- A notice sent _after_ the deadline does not discharge it either. It is still
  worth sending and the review still records it, as `kept` and `late`; what it
  is not is the duty met, and `discharged` is the field that says so.
- A notice cannot discharge a duty owed to somebody else. Where the records say
  the material opened belonged to a person the grant does not name, they are the
  people owed, and the episode's single notice is not evidence they were told.

**Know the delivery gap before you rely on this step.** There is no per-person
channel in this workspace. The platform's status banner is a broadcast, and the
only customer-facing reader of authored banners drops every targeted one (see
the same note in `incident-runbooks.md`). So `disclosureOwed` returns a
statement and a deadline and nothing sends it: send the words yourself, by the
contact route the tenancy agreement names for the institution, and record that
you did in the case. A notice nobody can see is the failure this step exists to
prevent, not a lighter version of it.

---

## 6. Reviewing the episode afterwards

`reviewSupportEpisode` re-decides the whole episode from its records and returns
one of seven standings, most serious first: `bypassed`, `undisclosed`,
`unclosed`, `probed`, `unused`, `purpose-unserved`, `bounded`.

Two are worth understanding before you see them.

**`bypassed`** means either that a record says a read was allowed which no
window this grant ever held could have allowed, or that a record disagrees with
the grant it cites — a different requester, a different case, or an audited
action that does not match its own class. Neither is a policy change and neither
is a paperwork error: both mean the read path did not go through the gate, and
once that is true nothing else the records say can be taken at face value. The
review deliberately re-decides only the faults that were fixed at the moment
they happened — a grant that has since been narrowed does not turn six months of
legitimate reads into accusations.

**`undisclosed` outranks `probed`.** Somebody reaching past the grant and being
stopped is the mechanism working. Somebody's material being read and the person
not being told by the day it was owed is a duty to a real person that was not
kept, and it is reported first even when both are true of the same episode.

**`purpose-unserved`** means the window was opened citing a purpose and every
read it was used for could have been made under any of the four. The purpose did
no work, which usually means the window was wider than the case needed.

`clauseReach` answers the question an administrator cannot answer from four
checkboxes: what those purposes actually reach here, which of them belong to an
identified person, and which adopted purposes are the same permission with two
words on it.

---

## 7. What this procedure does not yet have

Recorded plainly, because a procedure that overstates itself is worse than none.

- **No drill has been run.** Nothing here has been followed by a person under
  time and measured against the response objective. YSD-22058's `support` entry
  stays open for exactly that reason.
- **No support console exists in this repository.** `support-access.ts` has no
  caller outside its own spec, and the endpoint that decides support access
  today — `POST /api/study/institutional-controls/decisions` — decides and does
  not hold what it decided. Until something wires it, the grant, the read
  records and the revocation are kept by whoever runs this procedure.
- **The notice has no transport**, as §5 says.
