Version: 1.0.0Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Created: 2026-02-05
Last Updated: 2026-02-05
Status: ✅ Phase 1 Complete; Phase 2 Complete
Owner: HR (Workforce & HRIS)
Spec Reference: HR-21-job-descriptions-agreements.md
Overview
This document formalizes every integration point for HR-21 (Job Descriptions & Agreements): platform dependencies (PF-64, PF-10, PF-17, Platform Signatures), HR-internal (HR-01, HR-12, hr_positions), and downstream (HR-09). It defines request/response schemas, event payloads, authentication/authorization and tenant isolation, retry/idempotency, and PDF generation and document-signing security. Constitution Reference: Section 1.3 (Integration Patterns). HR-21 integrates via Platform Integration Layer and platform capabilities only; no direct cross-core dependencies.1. PF-64 (Document Templates / Letterhead)
Purpose
Job description PDFs use organization letterhead and optional PF-64 document template for layout. Content comes fromhr_job_description_agreements.content_snapshot and position/employee context.
Contract Type
Consumption of PF-64 letterhead and optional template; no new PF-64 APIs. PDF generation Edge Function uses existing patterns (e.g.generate-templated-pdf, generate-signed-pdf).
Request/Response (PDF generation Edge Function → letterhead/template)
Input (internal to Edge Function):organization_id(UUID) – tenant; used to resolve default letterhead and template.content_snapshot(JSONB) – fromhr_job_description_agreements.content_snapshot; structure:{ summary, responsibilities, qualifications, reporting?, physical? }.- Optional:
template_id(UUID) – PF-64 template id when org uses a job-description template; otherwise letterhead-only layout.
- Query letterhead by
organization_idandis_default = true(or equivalent); no direct RLS-protected table access from Edge Function – use service role or SECURITY DEFINER API that returns letterhead for org.
- PDF bytes generated; upload to storage; return
storage_pathand metadata forpf_documentsinsert.
- 403 if org has no letterhead/template access; 500 with sanitized message for rendering failures; no PHI in error payload.
Authentication / Tenant isolation
- Edge Function invoked with JWT or service role. Resolve
organization_idfrom agreement row (or from validated request body); never trust client-suppliedorganization_idwithout verifying user has access. All letterhead/template reads scoped byorganization_id.
Retry / Idempotency
- PDF generation: idempotent for same
agreement_id+content_snapshot; caller can retry with same inputs. Store result inpf_documentsand sethr_job_description_agreements.generated_document_idonce; duplicate calls can overwrite or skip if document already exists (documented in implementation).
PDF generation and storage security
- Generated PDF must be stored in a bucket and record created in
pf_documentswith correctorganization_id,custom_fields.employee_id, anddocument_kind: 'job_description'. Access to the file must follow RLS onpf_documentsand storage policies; only the employee (self) and users with org access and appropriate permissions can read.
Migration / compatibility (multi-tenant)
- New orgs: ensure default letterhead exists or PDF generation degrades gracefully (e.g. no letterhead). Existing orgs: no schema change to PF-64; optional Phase 2 job-description template per org.
2. PF-10 (Notifications)
Purpose
Notify manager when a job description is ready for signature; notify employee when ready to sign and when the signed PDF has been added to their file.Contract Type
Event-driven or direct call to platform notification API (PF-10). No new event channels required if PF-10 accepts in-app or email notifications by user/role.Request (HR-21 → PF-10)
Notify manager – “Job description ready for your signature”:Response / Success and error flows
- Success: notification queued or sent (per PF-10 contract).
- Error: 429 rate limit → retry with backoff; 4xx → do not retry; log and surface sanitized message.
Authentication / Tenant isolation
- Caller (HR app or Edge Function) must have validated
organization_idand that the recipient belongs to that org. No cross-tenant notification.
Retry / Idempotency
- Notifications are best-effort; idempotency key by (agreement_id, notification_type, recipient) avoids duplicate in-app toasts; email may still duplicate on retry unless PF-10 supports idempotency keys.
Migration / compatibility
- PF-10 must support targeting by user id and optional template keys. No change to PF-10 schema for Phase 1 if current API accepts recipient + message payload.
3. PF-17 (Field Configuration)
Purpose
Addposition_id to hr_employees field config (STANDARD_FIELDS or org-specific) so the employee form shows a position dropdown.
Contract Type
Configuration only. HR-21 consumes PF-17 field config; no new API. Field config drives which fields appear on the employee create/edit form.API / Data contract
- Entity:
hr_employees. - Field:
position_id(UUID, FK tohr_positions.id). - Config: Include in STANDARD_FIELDS or in org-specific override so that the field is visible and editable where appropriate.
- Lookup: Position dropdown options from
hr_positionsfiltered byorganization_idand active status; list includes at leastid,title(or equivalent), and optionallyjob_description_fullfor display.
Request/Response
- No direct request/response; form reads field config and loads positions via existing data hooks (e.g.
usePositionList()or equivalent) withorganization_idfrom context.
Authentication / Tenant isolation
- Field config and position list scoped by
organization_id; RLS and defense-in-depth on all reads.
Migration / compatibility
- New deployments: add
position_idto default field config forhr_employees. Existing orgs: optional migration or admin UI to add field to config; existing employee records haveposition_idNULL until set.
4. Platform Signatures (SignatureDialog, useSignature)
Purpose
Manager and employee sign the job description using the same pattern as offer letter and oversight session: SignatureDialog and useSignature for capture and storage of signature (e.g. signature record id and timestamp).Contract Type
UI and hook consumption from platform. No new platform API; HR-21 uses existing signature capture and optional storage of signature id inhr_job_description_agreements (manager_signature_id, employee_signature_id).
Signature dialog flow
- User opens sign page for agreement (manager or employee role).
- App loads agreement (read-only) and shows content + SignatureDialog.
- User signs; platform records signature (e.g. in signatures table or storage) and returns signature id/token.
- App updates agreement: sets manager_signed_at / manager_signature_id or employee_signed_at / employee_signature_id and advances status (e.g. pending_manager_signature → pending_employee_signature → signed).
Signature token exchange and storage
- Token exchange: If the platform uses a short-lived token for the signing session, it must be exchanged server-side or via a secure hook; no long-lived signature secrets in client.
- Storage of signed docs: Signed PDF is generated only after both signatures; PDF stored in
pf_documentswith link to employee; signature IDs stored onhr_job_description_agreementsfor audit. - Security: Only the manager of the employee (or HR admin) can record manager signature; only the employee (self) can record employee signature; enforced by RLS and application checks before calling signature API.
Request/Response (conceptual)
- Open dialog: No formal API; component receives agreement id and role.
- On sign complete: Callback receives signature id (and optionally timestamp); app then PATCHes agreement row with signature fields and status.
Authentication / Authorization scopes
- User must be authenticated; authorization: manager for direct-report agreements or employee for own agreement (and HR admin for manager path). Scopes align with RLS on
hr_job_description_agreements.
Tenant isolation
- Agreement and signature actions scoped by
organization_id; no cross-org access.
Migration / compatibility
- No platform schema change; ensure SignatureDialog and useSignature accept org/tenant context if needed for multi-tenant isolation.
5. HR-01 (Employee Directory)
Purpose
Employee and manager resolution;hr_employees.position_id and job_title; used when creating/editing agreements and when resolving “manager” for notifications and signature workflow.
Contract Type
Data dependency on HR-01 tables and APIs. HR-21 readshr_employees (and related profile/organization) for employee list, manager resolution, and position assignment.
Request/Response (data shape)
-
Employee record (relevant fields):
id,organization_id,position_id,job_title,reports_to_id(or equivalent for manager), and profile/user linkage for notifications and RLS. -
Position record (from hr_positions):
id,organization_id,title,description,job_description_full(Phase 1). Used in position dropdown and as source for job description content when creating an agreement.
Example (agreement creation)
- User selects employee and position.
- App loads
hr_employeesrow for employee (ensureposition_idandjob_titlecan be updated). - App loads
hr_positionsrow for selected position to getjob_description_full(and optional sections) for pre-fillingcontent_snapshotor for display. - Create
hr_job_description_agreementswithemployee_id,position_id,content_snapshot,status = 'draft'or'pending_manager_signature'.
Authentication / Tenant isolation
- All reads and writes scoped by
organization_id; RLS onhr_employeesandhr_positions; defense-in-depth in mutations.
Migration / compatibility
hr_positions.job_description_fulladded in HR-21 migration; existing positions have NULL until edited.hr_employees.position_idmay already exist; PF-17 config makes it visible on form.
6. HR-12 (My Documents)
Purpose
Extend useMyDocuments (and UI) so documents withdocument_kind: 'job_description' and link to current employee appear in My Documents (personnel file).
Contract Type
Consumption of HR-12 My Documents list andpf_documents query pattern. HR-21 writes pf_documents rows when a job description PDF is generated; HR-12 (or shared hook) includes those in the employee’s document list.
Request/Response (useMyDocuments extension)
- Query: Same as existing My Documents: filter by current user’s employee id (or profile) and optionally by
document_kind. - Include:
pf_documentsrows wherecustom_fields.employee_id= current employee id andcustom_fields.document_kind='job_description'(or equivalent tag). - Record shape (pf_documents):
id,organization_id,name,storage_path,file_type,custom_fields(e.g.{ employee_id, document_kind: 'job_description', agreement_id?, position_title? }),created_at, etc. RLS onpf_documentsenforces tenant and, if applicable, employee-scoped access.
Success / Error flows
- List returns combined personal uploads and job description PDFs; errors from Supabase follow standard error handling; sanitize user-facing messages.
Authentication / Tenant isolation
- User sees only documents for their org and (for employee self-service) their own employee_id in custom_fields. RLS and application filters enforce this.
Migration / compatibility
- Existing My Documents unchanged; extension is additive (new document_kind and optional filters). Multi-tenant: each org’s documents isolated by
organization_id; employee-scoped bycustom_fields.employee_id.
7. HR-09 (ATS – Consumer / Downstream)
Purpose
Future “Copy from position job description” when creating a job posting. Out of scope for Phase 1; contract defined for compatibility.Contract Type
Downstream consumption of position job description data. HR-09 (or ATS feature) will readhr_positions.job_description_full (and optionally agreement content) when creating a job posting.
Request/Response (future)
- Read:
hr_positions.id,job_description_full,organization_idfor a given position. - Authorization: Same org and ATS permission; no new API in Phase 1.
Event payload (if used later)
- Not required for Phase 1. If ATS subscribes to “position updated” events, payload could include
position_id,organization_id,job_description_full(or a flag that it changed). Version and compatibility notes: when HR-09 implements “copy from position”, usehr_positions.job_description_fulland document any versioning of that field.
Migration / compatibility
- Phase 1 adds
job_description_full; ATS can adopt when ready. Multi-tenant: ATS already scoped by org; no additional isolation required.
Phase 2: HR-09 Copy from position
Spec Reference: HR-21-PHASE-2-EXPANSION.md Purpose: When creating or editing a job posting in HR-09, the user can choose “Copy from position” to pre-fill the posting description from a selected position’s full job description. Same-core read; no new API or event. Data source:hr_positions.job_description_full (and optionally hr_positions.title for display). Phase 2 may also add job_description_updated_at / job_description_version; consumer may use these for “last updated” display.
Consumer: HR-09 job posting create/edit form. User selects a position (from active hr_positions for the org); client or hook reads hr_positions row by id and organization_id; description field is pre-filled from job_description_full. No server-to-server call; same Supabase RLS and organization_id context as rest of HR.
Pattern: Same-core read. HR-09 code (or shared HR data layer) queries hr_positions with organization_id filter; RLS enforces tenant isolation. No direct cross-core import; both HR-21 and HR-09 are HR core. No new event contract; no new API endpoint.
Authentication / Tenant isolation: Caller must have org access; query uses organization_id from context. RLS on hr_positions enforces isolation. No PHI in job description content beyond role titles; document access logging per HR-12 pattern applies to generated PDFs only, not this read path.
8. Cross-cutting: PDF generation and document-signing security
PDF generation
- Current (Phase 1/2): Client-triggered via
useJobDescriptionPdfhook usinguseGenerateTemplatedPdffrom@/platform/templates. Letterhead resolved from org default orhr_module_settings.job_description_pdf_template_id(Phase 2 T11). - Trigger: When agreement status becomes
signed(both manager and employee signed), the client generates the PDF. - Input:
agreement_id,content_snapshot,organization_id,employee_id, position title (for display). - Steps: Resolve letterhead (PF-64); render PDF; upload to storage; insert
pf_documentswithorganization_id,custom_fields.employee_id,document_kind: 'job_description', and optionalagreement_id; updatehr_job_description_agreements.generated_document_id. - Security: Edge Function or backend only for server-side path; validate agreement belongs to org and user has access; never expose internal paths or keys to client.
Async PDF generation (Future — Documented per T10)
Two options are documented for future server-side PDF generation when the client-triggered path is insufficient: Option A: Database trigger (recommended future default)- A
AFTER UPDATEtrigger onhr_job_description_agreementsfires whenNEW.status = 'signed' AND OLD.status <> 'signed'. - Trigger calls a SECURITY DEFINER function that invokes an edge function (e.g.,
generate-job-description-pdf) viapg_netHTTP extension. - Prerequisite: A server-side edge function that replicates
buildJobDescriptionContent+ templated PDF generation must be created first. This edge function does not yet exist. - Error handling: Log server-side; show “PDF could not be generated” in agreement view; optional retry/support hint.
- UX: Help text “Signed PDF will appear in My Documents shortly.” on agreement detail page.
- A scheduled edge function (cron) polls for agreements with
status = 'signed'andgenerated_document_id IS NULL. - Generates PDF for each, updates
generated_document_id. - Advantage: No
pg_netdependency; simpler error handling and retry. - Disadvantage: Latency (depends on cron interval).
Signature dialog flow and signature token exchange
- Flow: Manager signs first → status to pending_employee_signature; employee signs → status to signed.
- Token exchange: If platform uses tokens for signing, exchange server-side; store only signature id (and timestamp) on agreement.
- Storage of signed docs: Final PDF generated after both signatures; stored in
pf_documentslinked to employee; access via RLS and HR-12 My Documents.
Tenant isolation (all integrations)
- Every integration point must enforce
organization_idand, where applicable, employee or manager scope. RLS onhr_job_description_agreements,hr_employees,hr_positions, andpf_documentsis the baseline; application code must also filter byorganization_idfor mutations (defense-in-depth).
9. Summary table
| Dependency | Type | Request/Response / Event | Auth / Tenant | Retry / Idempotency |
|---|---|---|---|---|
| PF-64 | Consumption | Letterhead/template by org | Org-scoped | Idempotent by agreement + content |
| PF-10 | Notifications | Payloads above | Org + recipient | Best-effort; optional idempotency key |
| PF-17 | Config | Field config for position_id | Org-scoped | N/A |
| Platform Signatures | UI/hook | Dialog + callback | Manager/employee/HR | N/A |
| HR-01 | Data | hr_employees, hr_positions | Org + RLS | Standard CRUD |
| HR-12 | Data/UI | useMyDocuments + pf_documents | Org + employee | Standard query |
| HR-09 | Downstream | Read job_description_full; Phase 2: Copy from position (same-core read) | Org-scoped | N/A Phase 1; Phase 2 same-core |