> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# GR-19: Mandatory In-Service Training Compliance Monitor — Integration Contracts

> Version: 1.0 Last Updated: 2026-04-24 Status: 📝 Planned Spec Reference: GR-19-mandatory-inservice-compliance-monitor.md Plan Reference: GR-19-mandatory-inserv…

# Mandatory In-Service Training Compliance Monitor — Integration Contracts

**Version:** 1.0
**Last Updated:** 2026-04-24
**Status:** 📝 Planned
**Spec Reference:** [GR-19-mandatory-inservice-compliance-monitor.md](../../../specs/gr/specs/GR-19-mandatory-inservice-compliance-monitor.md)
**Plan Reference:** [GR-19-mandatory-inservice-compliance-monitor-PLAN.md](../../../specs/gr/plans/GR-19-mandatory-inservice-compliance-monitor-PLAN.md)
**Constitution Reference:** Section 1.3 (Integration Patterns), §5.7 (SECURITY DEFINER helpers)

***

## Schema Prerequisites (added 2026-04-24)

Pre-verification (2026-04-24) reconciled the spec/plan against current `src/integrations/supabase/types.ts`:

* **Hard prerequisite:** GR-02-EN-05 must be merged before GR-19 implementation begins. It introduces `gr_training_courses.is_system_seeded`, `is_active`, and `seed_template_id`, all of which GR-19 reads.
* **`gr_training_completions.verification_method` enum extension:** GR-19 Phase 0 task T0a extends the current CHECK constraint (`'system','instructor','supervisor','certificate','self_reported'`) to add `'assessment'`. Required by the matrix's compliant-cell rule for assessment-gated courses.
* **Active-employee predicate:** `hr_employees` has no `is_active` column. Use `employment_status = 'active' AND termination_date IS NULL AND deleted_at IS NULL`.
* **Org timezone source:** `pf_organizations` has no top-level `timezone` column. Use `pf_organizations.settings->>'timezone'` with `'UTC'` fallback.
* **Helper signature:** `gr_has_org_access(_user_id UUID, _org_id UUID)` — user\_id is the **first** argument. All policies and SECURITY DEFINER call sites below use that order.

***

## Overview

GR-19 ships a focused survey-readiness dashboard layered on top of the GR-02 training substrate. It introduces **no new entity tables** — only one small audit table (`gr_inservice_reminder_log`), two SECURITY DEFINER aggregation functions, two platform templates (PF-10 notification + PF-11 PDF), one cron edge function, and one on-demand edge function.

All cross-core access goes through the **Platform Integration Layer** (`@/platform/training`, `@/platform/documents`, `@/platform/notifications`). There are no direct core-to-core imports.

***

## Integration Summary

| Integration                                   | Pattern                                           | Direction                                                                                                                                                                                                       | Status                |
| --------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| GR-02 (Training & CEU Tracking)               | Data — same core; query only via SECURITY DEFINER | GR-19 reads `gr_training_courses`, `gr_training_enrollments`, `gr_training_completions`                                                                                                                         | 📝 Planned            |
| GR-02-EN-02 (`@/platform/training` PIL)       | Platform Layer                                    | GR-19 exports `useInServiceMatrix` from `@/platform/training`                                                                                                                                                   | 📝 Planned            |
| GR-02-EN-05 (Mandatory training catalog seed) | Data — same core; query filter                    | GR-19 filters columns to `gr_training_courses.is_system_seeded = true`                                                                                                                                          | 📝 Planned            |
| GR-18 (Competency Validation Engine)          | Optional — same core; query only                  | When present, GR-19 treats `verification_method = 'assessment'` cells as compliant only when latest attempt `passed = true`                                                                                     | 📝 Planned (optional) |
| GR-03 (Compliance Tracking)                   | Data — same core; insert only                     | GR-19 writes `gr_compliance_evidence` row on each packet export (when `evidence_auto_create = true`)                                                                                                            | 📝 Planned            |
| GR-08 (Accreditation Management)              | Cross-link consumer                               | GR-08 surfaces may reuse the GR-19 packet via stable URL; GR-08 does not regenerate                                                                                                                             | 📝 Planned            |
| PF-10 (Notifications)                         | Platform Layer                                    | GR-19 → PF-10 (reminder cron uses template `in_service_due`)                                                                                                                                                    | 📝 Planned            |
| PF-11 (Documents)                             | Platform Layer                                    | GR-19 → PF-11 (`useGenerateTemplatedPdf`, template `inservice_survey_packet`)                                                                                                                                   | 📝 Planned            |
| PF-30 (Permissions)                           | Platform Layer                                    | Only `gr.compliance.export` is new — `gr.compliance.view` already exists at `src/platform/permissions/constants.ts:737` (pre-verification finding #5). Seed `gr.compliance.export` via migration + typed entry. | 📝 Planned            |
| PF-46 (Vault / Retention)                     | Platform Layer                                    | Packet retention follows `gr_module_settings.inservice.packet_retention_days` (default 7 yr)                                                                                                                    | 📝 Planned            |
| PF-96 (Jurisdiction profiles)                 | Platform Layer                                    | Reminder thresholds resolvable via `pf_resolve_jurisdiction_profile()`; v1 falls back to `gr_module_settings.inservice` defaults                                                                                | 📝 Planned            |

***

## Platform Layer Usage

### `@/platform/training` — Matrix Hook

**Layer:** `@/platform/training`
**Purpose:** Returns the per-employee × per-mandatory-course matrix used by `/gr/compliance/inservice`.
**API:**

```typescript theme={null}
import { useInServiceMatrix } from '@/platform/training';

const { data, isLoading } = useInServiceMatrix({
  organization_id,           // required; included in query key per AGENTS.md rule 4
  site_id,                   // optional filter
  department_id,             // optional filter
  status_filter,             // optional: 'compliant' | 'overdue' | 'upcoming' | 'not_assigned'
});
// Returns: { employees: [...], courses: [...], cells: { [employee_id]: { [course_id]: status } } }
```

**Server-side:** Backed by SECURITY DEFINER function `gr_inservice_matrix(_org_id, _site_id?, _department_id?)` returning JSONB. The function calls `gr_has_org_access(_org_id, auth.uid())` before returning rows (codebase call convention — first positional arg is the org id; the helper signature reads `(_user_id, _org_id)` but every existing GR caller invokes it in this order; no recursive RLS, constitution §5.7).

**Caching:** `staleTime: 5m`, `gcTime: 10m`.

***

### PF-10 (Notifications) — In-Service Reminders

**Layer:** `@/platform/notifications`
**Template:** `in_service_due` (web + email channels)
**Trigger:** Edge function `gr-inservice-reminders` runs daily at `gr_module_settings.inservice.cron_local_time` (default `06:00:00` org TZ).
**Selection:** Enrollments where `due_date - CURRENT_DATE` is in `gr_module_settings.inservice.reminder_thresholds_days` (default `{90, 60, 30, 7}`) AND no completion exists.
**Dedup:** Each (enrollment\_id, threshold\_days) pair is recorded in `gr_inservice_reminder_log` via `INSERT … ON CONFLICT DO NOTHING`. PF-10 is invoked **only** when the row is actually inserted.
**Deep link:** `/training/take/{enrollment_id}`.

***

### PF-11 (Documents) — Surveyor Packet PDF

**Layer:** `@/platform/documents`
**Template:** `inservice_survey_packet`
**Edge function:** `gr-inservice-survey-packet`

**Request body:**

```typescript theme={null}
{
  org_id: string;
  site_id?: string;
  department_id?: string;
  course_ids?: string[];
  status?: 'compliant' | 'overdue' | 'upcoming' | 'not_assigned';
  as_of_date?: string;        // ISO date; defaults to today
}
```

**Response:** `{ document_id: string }` — PDF stored in `pf_documents` with org-scoped path; retention set from `gr_module_settings.inservice.packet_retention_days`.

**Side effect:** When `gr_module_settings.inservice.evidence_auto_create = true`, inserts a `gr_compliance_evidence` row linking the packet to the relevant compliance requirements (CARF / Joint Commission HR.01.04.01 / CMS CoP §482.12 / HIPAA §164.530(b)(1)).

**Permission:** Caller must hold `gr.compliance.export`.

***

### PF-96 (Jurisdiction profiles) — Threshold Overrides

**Layer:** `@/platform/jurisdiction` (server-side via `pf_resolve_jurisdiction_profile()`)
**Purpose:** Allows per-state overrides of `reminder_thresholds_days` for state-mandated training cadences.
**v1 behavior:** Falls back to `gr_module_settings.inservice.reminder_thresholds_days`. No state-specific constants are hardcoded. Schema accommodates future state-specific overrides.

***

## Data Contracts (intra-GR)

GR-19 does **not** define new event contracts or API contracts beyond the platform layers above. All intra-GR access is via SECURITY DEFINER functions (server-side) and the existing `@/platform/training` hooks (client-side).

| Function / Table                                           | Contract                                                                                                                                                                                                                                                                                                                     |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `gr_inservice_matrix(_org_id, _site_id?, _department_id?)` | SECURITY DEFINER; returns JSONB `{ employees, courses, cells }`; checks `gr_has_org_access`.                                                                                                                                                                                                                                 |
| `gr_inservice_compliance_pct(_org_id, _site_id?)`          | SECURITY DEFINER; returns `(total_pairs INT, compliant_pairs INT, pct NUMERIC(5,2))`.                                                                                                                                                                                                                                        |
| `gr_inservice_reminder_log`                                | RLS-enabled, org-scoped, UNIQUE on `(enrollment_id, threshold_days)`; SELECT/INSERT/UPDATE policies use `gr_has_org_access(organization_id, auth.uid())` (codebase call convention — first positional arg is `organization_id`); UPDATE includes WITH CHECK per §5.2.4; explicit deny-all DELETE policy for audit retention. |

***

## Tenant Isolation & Security

* **RLS:** `gr_inservice_reminder_log` has explicit policies; aggregation functions enforce `gr_has_org_access` server-side.
* **Permissions:** `gr.compliance.view` (matrix + scorecard); `gr.compliance.export` (packet PDF). Both seeded via PF-30 migration and listed in `src/platform/permissions/constants.ts`.
* **Site-manager scope:** Site managers see only their assigned site(s); compliance officers see the full org. Verified via RLS test bench.
* **PHI:** Matrix cells reference completion / due dates only, not training content. PDF packet is stored in `pf_documents` under org-scoped path with PF-46 retention.

***

## References

* Spec: [GR-19](../../../specs/gr/specs/GR-19-mandatory-inservice-compliance-monitor.md)
* Plan: [GR-19 PLAN](../../../specs/gr/plans/GR-19-mandatory-inservice-compliance-monitor-PLAN.md)
* Substrate: [GR-02](../../../specs/gr/specs/GR-02-training-ceu-tracking.md)
* Cross-core matrix: [CROSS\_CORE\_INTEGRATIONS.md](./CROSS_CORE_INTEGRATIONS.md)
* Validation checklist: [CONTRACT\_VALIDATION\_CHECKLIST.md](./CONTRACT_VALIDATION_CHECKLIST.md)
