# Policy Documents & Acknowledgments Workstream 11 — persistence of staff acknowledgment of policy/safety documents. ## Purpose Campus staff must acknowledge two categories of documents — **Safety Protocols** (official/government) and **Handbook & Policies** (internal). `director` and `office_manager` author the documents; all four campus staff roles (`director`, `office_manager`, `teacher`, `support_staff`) acknowledge them. Acknowledgment is **per document version**: editing a document bumps its `version`, which requires re-acknowledgment. ## Frontend wiring The two existing pages consume this single store (the old generated `documents` entity it replaced has been removed): - **Handbook & Policies** (`business/policies`) lists `policy_documents` of `category = handbook_policy`, mapping the handbook's sub-category to/from `tag` (`toPolicyViewModel` / `toPolicyDocumentMutationDto`). Management is gated to owner/superintendent/director/office_manager (`canManagePolicies`, mirroring the backend grant). Acknowledgment is **persisted** via `policy_acknowledgments` (`usePolicyAcknowledgments` / `useAcknowledgePolicy`), replacing the former local-state set. - **Safety Protocols** (`business/safety-protocols`) consumes `category = safety_protocol`, rendering author-filled `steps` + autism considerations with a static per-`tag` card icon (fire/shield/heart). It shares the persistent acknowledgment hooks. Both pages are seeded from `20260611050000-policy-documents-seed.ts` (safety protocols reuse the former content-catalog `safetyProtocols` payload; a few handbook policies seed the handbook page). The Safety Protocols page also has a manager-gated **structured-authoring** flow (mirrors the F.R.A.M.E. module: header *New Protocol* button → create form, per-card *Edit*/*Delete*) with **dynamic** `steps` + `autismConsiderations` rows that add/remove independently, so each protocol carries its own count (`useSafetyProtocolsModule` + `SafetyProtocolForm` / `SafetyDynamicListEditor`; gated by `canManageSafetyProtocols`, which reuses the policy grant). Title/body/steps/considerations changes bump `version` and require re-acknowledgment. ## Entities - `policy_documents` (generic-CRUD entity): `title`, `body`, `category` (`safety_protocol` | `handbook_policy` — selects the page), `tag` (nullable finer **sub-category**; the Handbook page maps its Operations/Behavior/Safety/Communication/Legal categorisation onto it, and the Safety page uses it to pick the static card icon), `author` (display name of the **creating user**, set server-side at creation and not changed on update), `steps` + `autism_considerations` (JSONB string arrays — **author-filled structured content** for safety protocols; null for handbook policies), `version` (bumped when `title`/`body`/`steps`/`autism_considerations` change), `active`, tenant `organizationId` + nullable `campusId`. This is the **single unified store** for both the Safety Protocols and Handbook & Policies pages (filter by `?category=` and optionally `?tag=`). The category **list** + icons are static frontend config; each document's category assignment (`tag`) is DB data. `author` is derived from the current user's name — `${name_prefix} firstName lastName` (the honorific title from `users.name_prefix`, e.g. "Dr. Sarah Williams"), else email. - `policy_acknowledgments` (per-user): one row per (`userId`, `policyDocumentId`, `version`), with `acknowledgedAt`. Unique index on those three columns; acknowledging is idempotent for a given version. ## Routes - `GET/POST /api/policy_documents`, `PUT/DELETE /api/policy_documents/:id`, plus the standard generic-CRUD extras — guarded by `checkCrudPermissions('policy_documents')` (`${METHOD}_POLICY_DOCUMENTS`). - `GET /api/policy_acknowledgments` (the caller's own acknowledgments) and `POST /api/policy_acknowledgments` (`{ data: { policyDocumentId } }` → acknowledges the document's **current** version) — both guarded by `checkPermissions('ACK_POLICY')`. ## Authorization - `READ_POLICY_DOCUMENTS` — granted to the four campus roles (director via full access; office_manager/teacher/support via the read-only entity grant). `student`/`guardian` get no policy-document access. - `CREATE/UPDATE/DELETE_POLICY_DOCUMENTS` — `director` (full access) and `office_manager` (explicit grant in the role seeder). `teacher`/`support_staff` are read-only. - `ACK_POLICY` — the four campus roles (a product-feature action permission; extendable per user via `custom_permissions`). Tenant/campus scoping is applied in the data layer (`tenantWhere` / `findOwnedByPk`); acknowledgment reads are additionally restricted to the caller's own `userId`. A manager-facing acknowledgment-status report (audience TBD) is a deferred refinement. ## Tests - **Unit** (`backend/src/shared/constants/policy-documents.test.ts` + `users.test.ts`, `npm test`): the pure domain rules — `isPolicyDocumentCategory` validation, the `nextPolicyDocumentVersion` re-acknowledgment bump, and `formatPersonName` (author rendering). - **Frontend unit** (`vitest`): `business/policies/mappers.test.ts` (handbook; tag↔category, author), `business/safety-protocols/mappers.test.ts` (steps + autism considerations) and `business/safety-protocols/selectors.test.ts` (management grant + draft validation for the authoring form). - **Seeded e2e** (`frontend/tests/e2e/policy-acknowledgments.seeded.e2e.ts`, `npm run test:e2e:content`): document create/persist, manage-vs-read RBAC (director/office_manager manage; teacher reads but cannot create), idempotent per-version acknowledgment, version-bump re-acknowledgment, and external-role lockout. ## Open / deferred - Acknowledgment-status reporting for managers (who-acknowledged-what) — pending the report-audience decision. - The acknowledgment + document-management **UI** is design-gated (see `docs/backlog.md`).