ERP HR Employee Lifecycle & Policy Operations Skill
Overview
This skill covers People Operations tasks in the Northwind People HRMS: onboarding closeout,
leave source precedence, policy case review (folder readiness + formal notice quality),
recruitment reconciliation, and payroll assignment/accrual readiness. The system exposes a
REST API and a set of interrelated records — employee profiles, payroll ledgers, cases,
policies, documents, messages, audits, and recruitment packets. Decisions always flow from
authoritative records (approved/submitted ledger assignments) over summary snapshots
(employee profiles, case summaries, messages alone).
API Usage Workflow
Base URL
Use the URL from environment_access.md. Do not use localhost or 127.0.0.1 unless that
URL itself points there.
Core Endpoints (all GET)
| Endpoint |
What it returns |
/api/manifest |
Schema version, seed, file counts, business modules |
/api/summary |
Aggregate counts by status, department list |
/api/employees |
All employee profiles (summary view; may be stale) |
/api/cases |
All cases with summary fields |
/api/cases/<case_id> |
Case detail: approvals, attachments, audit_events, comments, policy_refs |
/api/policies |
All policies at summary level |
/api/policies/<policy_id> |
Single policy with section headings and body text |
/api/payroll-ledgers |
Leave assignments, salary assignments, worksheets, adjustments |
/api/recruitment |
All openings with candidates, offer_register, cost_ledger, notice_packets |
/api/documents |
All document folders with files, tags, required_files, required_tags, ready flag |
/api/messages |
All formal notice messages with quality, defects, channel, status |
/api/notifications |
System notifications |
/api/audit |
All audit events (summary) |
/api/audit/<event_id> |
Single audit event detail |
/api/attachments/<attachment_id> |
Attachment text content (plain text response) |
Recommended Investigation Sequence
- Identify the subject (employee, case, or opening) from the task prompt.
- Fetch the employee profile (
/api/employees → filter by employee_id) for
baseline context — but NEVER treat it as authoritative when ledger records exist.
- Fetch the relevant ledger (
/api/payroll-ledgers → filter by employee_id and
record_type). Distinguish Leave assignment vs Salary assignment rows.
- Fetch the case (
/api/cases/<case_id>) for approvals, attachments, linked
audit events, and comments.
- Fetch policies referenced in the case's
policy_refs.
- Fetch documents (
/api/documents) for folder readiness checks.
- Fetch messages (
/api/messages) for formal notice quality checks.
- Fetch recruitment (
/api/recruitment) for candidate outcomes, offers, costs,
and notice packets.
- Cross-reference audit events — use
/api/audit for the global list and
/api/audit/<id> for detail. Audit events are scoped to a single concern; do not
mix leave-scope audits into document/notice decisions, or vice versa.
Business Rules by Module
A. Leave Assignment Source Precedence
Governing policy: LEAVE-SRC-001 §2.1 — "The latest approved or submitted leave
assignment for the period controls. Draft, voided, and obsolete records are excluded
even when profile summaries conflict."
Status precedence (highest to lowest):
Approved — authoritative
Superseded — replaced by a later assignment; exclude from current decisions
Draft — planning only; always exclude
Decision logic:
- Find all leave assignments for the employee in the current period (year
2026).
- Pick the latest
Approved record (by updated_at).
- If no
Approved exists, fall back to the latest Submitted record.
- Never use
Draft records.
- Never use
Superseded records when a newer Approved exists for the same period.
- The employee profile's
leave_balance_days and any inline policy reference are
secondary. When an approved assignment exists and the profile conflicts, the
profile is stale → profile_policy_ignored: true.
Key fields from ledger:
ledger_id → assignment_id
policy_name → effective_leave_policy
approved_leave_days → annual_days / balance_days
status → determines inclusion/exclusion
B. Payroll / Salary Assignment Precedence
Governing policy: PAY-SRC-001 §3.4 — "Use the current submitted salary assignment.
Draft planning assignments do not affect payroll readiness or accrual checks."
Status precedence:
Submitted — authoritative
Draft — always exclude
Decision logic:
- Find all salary assignments for the employee.
- Pick the
Submitted record.
- Exclude every
Draft record.
- Base salary comes from the
Submitted assignment's base_salary field.
- Effective date is the assignment's
updated_at date (date portion only).
Accrual readiness:
- An accrual batch is ready when a
Submitted salary assignment carries a non-null
accrual_batch_id.
- Draft assignments with accrual data do NOT make accrual ready.
C. Recruiting Payroll Handoff Gate
Governing policy: PAY-SRC-001 §4.2 — "Recruiting payroll handoff is created
only after a selected candidate has an accepted offer. The handoff must be submitted;
draft prechecks do not satisfy the assignment gate."
Decision logic:
- Only the selected candidate with an accepted offer triggers payroll handoff.
- The handoff action is
create_submitted_assignment_after_acceptance when the
selected candidate has accepted.
draft_payroll_allowed is always false — draft prechecks are not valid handoffs.
- Waitlisted and rejected candidates do not trigger payroll handoff.
D. Recruitment Reconciliation
Candidate classification:
committee_decision: "Selected" → selected candidate
committee_decision: "Waitlisted" → waitlisted candidate
committee_decision: "Rejected" → rejected candidate
Offer validation:
- The selected candidate must have an entry in
offer_register with status: "accepted".
offer_id and offer_base_salary come from the accepted offer.
Cost calculation:
recruitment_cost_total = sum of all amount values in cost_ledger.
- Source is always
recruitment_cost_ledger (never case summary).
Notice follow-up:
- Check
notice_packets for each non-selected candidate.
- If
status: "not_sent" and required_action says send → candidate ID goes in
notice_followup_required.
- Waitlisted candidates with unsent notices →
send_waitlist_notice.
- Rejected candidates with unsent notices →
send_rejection_notice.
- Notice quality is determined from
notice_packet_inspection (the notice_packets
array in the recruitment record), not from messages alone.
Source hierarchy for recruitment:
- Candidate status:
interview_feedback_and_offer (from recruitment endpoint's
candidates array and offer_register).
- Outcome control:
committee_decision_with_offer_confirmation.
- Cost:
recruitment_cost_ledger.
- Notice quality:
notice_packet_inspection.
E. Policy Case Folder Readiness
Governing policy: POL-DOCS-2026 §5.1 — "A folder is not ready unless all
required files and required tags shown in the folder checklist are present."
Decision logic:
folder_ready: true ONLY when all required_files are present in files AND
all required_tags are present in tags.
- Any missing file →
folder_ready: false, list missing files in missing_files.
- Any missing tag →
required_tag_present: false.
- Use the
/api/documents endpoint to find the folder associated with the case.
- Also check case
attachments of kind: "Checklist" — their content field
confirms what is missing.
F. Formal Notice Quality
Notice inspection sources (in priority order):
- Notice packet inspection — the
notice_packets array in /api/recruitment
or the messages endpoint (/api/messages) for policy cases.
- Message notice inspection — fallback when only messages are available.
- Case summary only — lowest confidence; do not rely on this.
Quality determination:
- A notice is
defective when its defects array is non-empty.
- If
quality field is explicitly set, use it; otherwise infer from defects.
- Defect types (from messages/notice_packets):
missing_ack_deadline — acknowledgement deadline absent
missing_appeal_instructions — appeal process not described
missing_waitlist_status — waitlist status omitted
missing_correct_policy — references wrong/legacy policy
Policy requirements (from HR-POL-014 §7.1):
International exception formal notices must include: executive approval reference,
time limits, tax equalization, VPN-only access, quarterly compliance review,
appeal instructions, and acknowledgement deadline.
G. Closeout / Final Control Gates
Approval closeout gate:
approval_sufficient_when_records_clean — all records are clean (no draft reliance,
no folder defects, no notice defects).
approval_not_sufficient_when_folder_or_notice_defective — any folder or notice
defect blocks close.
Final control result:
approve_closeout — all checks passed, records clean.
hold_for_folder_and_notice_defects — blocked by folder and/or notice issues.
ready_with_monitoring — records are correct but profile/system state needs
follow-up (e.g., stale profile summary, but assignment is clean).
Closeout blockers (can be multiple):
missing_required_files — folder missing required files.
missing_required_tags — folder missing required tags.
defective_formal_notice — formal notice has quality defects.
H. Cross-Module Escalation
When a case like XMODULE-77 references related audit events, inspect each
referenced event individually. Do not merge scopes:
- Leave-source audits → only for leave decisions.
- Document/notice audits → only for document/notice decisions.
- Payroll audits → only for payroll decisions.
Remediation ownership:
- Missing files/tags →
Records.
- Defective notices / policy issues →
People Ops Compliance.
- Payroll assignment issues →
Payroll QA.
Notice remediation actions:
reissue_defective_notices — when formal notice has defects.
send_new_offer_notice — when offer notice needs to be sent.
no_notice_action — when notices are clean.
Audit Event Model
Event Types and Their Scope
| Event |
Scope |
Use For |
leave.profile_mismatch |
leave_source_precedence_only |
Leave source decisions |
payroll.ready |
payroll_assignment_readiness |
Payroll readiness decisions |
payroll.draft_excluded |
payroll_assignment_readiness |
Payroll draft exclusion |
notice.defect |
document_notice_findings_only |
Notice quality decisions |
case.close_blocked |
document_notice_findings_only |
Folder/notice blocking decisions |
folder.tag_missing |
document_notice_findings_only |
Document/folder decisions |
cross_module.escalation_package |
cross-module (inspect each ref separately) |
Escalation triage |
Supporting vs Excluded Audit Events
When making a scoped decision:
- Supporting: Audit events whose scope matches the decision being made.
- Excluded: Audit events whose scope does NOT match (e.g., exclude
folder.tag_missing events from a leave_source_precedence_only decision).
Output Field Conventions
Normalized Enum Values
Precedence / source fields:
approved_assignment_over_profile — approved ledger assignment overrides stale profile.
employee_profile_summary — only when no ledger assignment exists.
case_summary_only — least authoritative; use only when nothing else is available.
approved_assignment_current_period — the approved assignment for the current period.
profile_summary_current_period — profile summary for the current period.
leave_assignment_history — leave data sourced from ledger leave assignments.
Payroll source status:
submitted — authoritative submitted assignment.
draft — excluded planning record.
superseded — replaced by a later record.
Draft exclusion rule:
exclude_draft_assignment — draft records are always excluded.
draft_allowed — drafts are permitted (rare; almost never used).
exclude_superseded_only — only superseded records excluded.
Audit scope (exactly one per decision):
leave_source_precedence_only
document_notice_findings_only
payroll_assignment_readiness
Closeout / control actions:
approve_onboarding_close — proceed with close.
block_close_and_reissue_notice — blocked; reissue notice.
open_records_remediation — open remediation workflow.
update_employee_summary — update stale profile.
no_action — nothing needed.
Recruitment-specific:
interview_feedback_and_offer — candidate outcome from interview + offer data.
committee_decision_with_offer_confirmation — committee outcome confirmed by offer.
notice_packet_inspection — notice quality from notice_packets array.
message_notice_inspection — notice quality from messages endpoint.
recruitment_cost_ledger — costs from cost_ledger array.
case_summary_only — fallback; least reliable.
Handoff / assignment gates:
create_submitted_assignment_after_acceptance — create submitted assignment once offer accepted.
create_payroll_precheck — precheck only (not a full handoff).
no_payroll_handoff — no handoff needed.
accepted_offer_only — only accepted-offer candidates for payroll.
accepted_offer_and_submitted_assignment — both conditions required.
all_interviewed_candidates — all interviewed (rare).
submitted_after_acceptance — submitted assignment required after acceptance.
submitted_handoff_required_after_acceptance — handoff must be submitted after acceptance.
submitted_handoff_required — submitted handoff generally required.
no_handoff_required — no handoff needed.
Follow-up actions (recruitment):
send_waitlist_notice — send waitlist notification.
send_rejection_notice — send rejection notification.
reissue_waitlist_notice_not_rejection — reissue notice without changing to rejection.
reissue_rejection_notice — reissue rejection notice.
no_action — no follow-up needed.
Offer exclusion reasons:
no_accepted_status_or_offer — no accepted offer exists.
waitlisted_not_selected — candidate was waitlisted, not selected.
already_rejected — candidate already rejected.
Date, Calculation, and Sorting Rules
Dates
- All dates are in ISO 8601 format:
YYYY-MM-DD for date-only, with T separator
for timestamps (e.g., 2026-03-01T09:00).
effective_date for payroll assignments: use the updated_at date portion
(e.g., 2026-04-01 from 2026-04-01T09:30).
- For leave assignments, the controlling period is
2026 — filter by period: "2026"
or the start of the period year.
Sorting / "Latest"
- When multiple assignments exist, the latest is determined by
updated_at
timestamp (not ledger_id order).
- For leave: pick the most recent
Approved by updated_at for the current period.
- For payroll: pick the
Submitted record (there is typically one).
Calculations
- Recruitment cost: Sum all
amount values in cost_ledger. Use exact arithmetic;
do not round.
- Annual leave days / balance days: Use
approved_leave_days from the authoritative
leave assignment. Do NOT use worksheet_leave_days from the ledger (that is a
different field for worksheet purposes).
- For salary assignments,
approved_leave_days and worksheet_leave_days are 0 —
ignore them; use base_salary.
Candidate Arrays
- Arrays for
waitlisted_candidates, rejected_candidates, and
notice_followup_required must contain candidate IDs only (e.g., "CAND-DA-7702"),
not names.
Source Precedence (Cross-Module)
When multiple data sources conflict, resolve in this order:
For Leave Entitlement
- Approved leave assignment in payroll ledger (highest)
- Submitted leave assignment in payroll ledger
- Employee profile summary (lowest; stale when ledger exists)
For Base Salary
- Submitted salary assignment in payroll ledger (highest)
- Employee profile salary_band (descriptive only; not a dollar amount)
- Draft salary assignment (excluded)
For Candidate Outcomes
- Recruitment endpoint:
candidates[].committee_decision + offer_register[].status
(highest)
- Audit events referencing the recruitment
- Case summary (lowest)
For Notice Quality
notice_packets in recruitment, or messages endpoint for policy cases (highest)
- Audit events on notice defects (corroborating)
- Case summary (lowest)
For Folder Readiness
/api/documents folder record (highest)
- Case attachment of kind
"Checklist" (corroborating)
- Case summary (lowest)
Evidence Source Order (for case review)
approval_history_folder_notice_audit — full chain: approvals → folder → notice → audit.
folder_notice_audit — folder → notice → audit (no approvals).
audit_only — audit events only.
Common Pitfalls
Draft contamination: Always filter out Draft status records from both leave
and payroll ledgers. Draft records are planning placeholders and have no
authoritative weight. This is the single most common error.
Profile staleness: Employee profile leave_balance_days may not match the
approved assignment. When a ledger assignment exists with a different value, the
profile is stale. Set profile_policy_ignored: true and use the ledger value.
Scope mixing: Do not use document/notice audit events (e.g., folder.tag_missing,
notice.defect) to support leave-source-precedence decisions, or vice versa. Each
audit event has exactly one scope. supporting_audit_event_ids and
excluded_audit_event_ids must respect this boundary.
Superseded vs Draft confusion: Superseded means a record was replaced by a
newer one — it was once valid but is now obsolete. Draft means it was never valid.
Both are excluded from current decisions, but they are semantically different.
Only Superseded goes in exclude_superseded_only lists.
Cost sum precision: Sum all cost_ledger amounts exactly as stored. Do not
estimate, round, or omit line items.
Tag vs file confusion: folder_ready requires BOTH all required_files AND all
required_tags. A folder with all files but a missing tag is NOT ready. Check both
independently.
Waitlist notice reissue vs rejection: When a waitlisted candidate's notice is
defective, the action is reissue_waitlist_notice_not_rejection — reissue the
waitlist notice without converting it to a rejection. Do not confuse this with
send_rejection_notice (for rejected candidates).
Recruitment notice packets vs messages: For recruitment tasks, notice quality
comes from notice_packets in the recruitment endpoint, NOT from the general
/api/messages endpoint (unless the notice_packets reference a specific
message_id). For policy cases, notice quality comes from /api/messages.
Payroll handoff requires accepted offer: A candidate who is "Selected" but
whose offer is still draft or absent does NOT trigger payroll handoff. The offer
must be accepted.
Audit detail vs audit list: /api/audit/<event_id> returns the same fields
as the entry in /api/audit or in a case's audit_events. The detail endpoint
is useful for confirming an event exists and reading its full detail field.
Employee ID in case vs employee ID in task: Cases like CASE-118 have
employee_id: "EMP-118" (the subject employee). But audit events attached to the
case may reference different entities. Always match on the task's target employee.
Folder document lookup: A case may reference a folder in its attachments
(kind: "Checklist"), but the authoritative folder record is in /api/documents.
Cross-reference both — the document endpoint provides required_files,
required_tags, files, tags, and ready; the attachment provides a human
summary confirming what is missing.
Multiple assignments for same employee: An employee may have multiple leave
assignments or salary assignments. Always select based on status precedence and
recency, not by picking the first one returned.
Policy reference without case detail: When a case lists policy_refs, fetch
each policy at /api/policies/<policy_id> to read the section text. The policy
body text contains the authoritative business rule.
worksheet_leave_days vs approved_leave_days: In leave assignment records,
use approved_leave_days for the employee's leave entitlement. The
worksheet_leave_days field is a separate worksheet tracking value and should not
be used for entitlement decisions.
1---2name: self-attempt-02-533description: ERP HR Employee Lifecycle & Policy Operations Skill4---5# ERP HR Employee Lifecycle & Policy Operations Skill67## Overview89This skill covers People Operations tasks in the Northwind People HRMS: onboarding closeout,10leave source precedence, policy case review (folder readiness + formal notice quality),11recruitment reconciliation, and payroll assignment/accrual readiness. The system exposes a12REST API and a set of interrelated records — employee profiles, payroll ledgers, cases,13policies, documents, messages, audits, and recruitment packets. Decisions always flow from14**authoritative records** (approved/submitted ledger assignments) over summary snapshots15(employee profiles, case summaries, messages alone).1617---1819## API Usage Workflow2021### Base URL22Use the URL from `environment_access.md`. Do not use localhost or 127.0.0.1 unless that23URL itself points there.2425### Core Endpoints (all GET)2627| Endpoint | What it returns |28|---|---|29| `/api/manifest` | Schema version, seed, file counts, business modules |30| `/api/summary` | Aggregate counts by status, department list |31| `/api/employees` | All employee profiles (summary view; may be stale) |32| `/api/cases` | All cases with summary fields |33| `/api/cases/<case_id>` | Case detail: approvals, attachments, audit_events, comments, policy_refs |34| `/api/policies` | All policies at summary level |35| `/api/policies/<policy_id>` | Single policy with section headings and body text |36| `/api/payroll-ledgers` | Leave assignments, salary assignments, worksheets, adjustments |37| `/api/recruitment` | All openings with candidates, offer_register, cost_ledger, notice_packets |38| `/api/documents` | All document folders with files, tags, required_files, required_tags, ready flag |39| `/api/messages` | All formal notice messages with quality, defects, channel, status |40| `/api/notifications` | System notifications |41| `/api/audit` | All audit events (summary) |42| `/api/audit/<event_id>` | Single audit event detail |43| `/api/attachments/<attachment_id>` | Attachment text content (plain text response) |4445### Recommended Investigation Sequence46471. **Identify the subject** (employee, case, or opening) from the task prompt.482. **Fetch the employee profile** (`/api/employees` → filter by `employee_id`) for49 baseline context — but NEVER treat it as authoritative when ledger records exist.503. **Fetch the relevant ledger** (`/api/payroll-ledgers` → filter by `employee_id` and51 `record_type`). Distinguish Leave assignment vs Salary assignment rows.524. **Fetch the case** (`/api/cases/<case_id>`) for approvals, attachments, linked53 audit events, and comments.545. **Fetch policies** referenced in the case's `policy_refs`.556. **Fetch documents** (`/api/documents`) for folder readiness checks.567. **Fetch messages** (`/api/messages`) for formal notice quality checks.578. **Fetch recruitment** (`/api/recruitment`) for candidate outcomes, offers, costs,58 and notice packets.599. **Cross-reference audit events** — use `/api/audit` for the global list and60 `/api/audit/<id>` for detail. Audit events are scoped to a single concern; do not61 mix leave-scope audits into document/notice decisions, or vice versa.6263---6465## Business Rules by Module6667### A. Leave Assignment Source Precedence6869**Governing policy**: `LEAVE-SRC-001` §2.1 — *"The latest approved or submitted leave70assignment for the period controls. Draft, voided, and obsolete records are excluded71even when profile summaries conflict."*7273**Status precedence** (highest to lowest):741. `Approved` — authoritative752. `Superseded` — replaced by a later assignment; exclude from current decisions763. `Draft` — planning only; always exclude7778**Decision logic**:79- Find all leave assignments for the employee in the current period (year `2026`).80- Pick the latest `Approved` record (by `updated_at`).81- If no `Approved` exists, fall back to the latest `Submitted` record.82- **Never** use `Draft` records.83- **Never** use `Superseded` records when a newer `Approved` exists for the same period.84- The employee profile's `leave_balance_days` and any inline policy reference are85 **secondary**. When an approved assignment exists and the profile conflicts, the86 profile is stale → `profile_policy_ignored: true`.8788**Key fields from ledger**:89- `ledger_id` → `assignment_id`90- `policy_name` → `effective_leave_policy`91- `approved_leave_days` → `annual_days` / `balance_days`92- `status` → determines inclusion/exclusion9394### B. Payroll / Salary Assignment Precedence9596**Governing policy**: `PAY-SRC-001` §3.4 — *"Use the current submitted salary assignment.97Draft planning assignments do not affect payroll readiness or accrual checks."*9899**Status precedence**:1001. `Submitted` — authoritative1012. `Draft` — always exclude102103**Decision logic**:104- Find all salary assignments for the employee.105- Pick the `Submitted` record.106- Exclude every `Draft` record.107- Base salary comes from the `Submitted` assignment's `base_salary` field.108- Effective date is the assignment's `updated_at` date (date portion only).109110**Accrual readiness**:111- An accrual batch is ready when a `Submitted` salary assignment carries a non-null112 `accrual_batch_id`.113- Draft assignments with accrual data do NOT make accrual ready.114115### C. Recruiting Payroll Handoff Gate116117**Governing policy**: `PAY-SRC-001` §4.2 — *"Recruiting payroll handoff is created118only after a selected candidate has an accepted offer. The handoff must be submitted;119draft prechecks do not satisfy the assignment gate."*120121**Decision logic**:122- Only the **selected** candidate with an **accepted** offer triggers payroll handoff.123- The handoff action is `create_submitted_assignment_after_acceptance` when the124 selected candidate has accepted.125- `draft_payroll_allowed` is always `false` — draft prechecks are not valid handoffs.126- Waitlisted and rejected candidates do not trigger payroll handoff.127128### D. Recruitment Reconciliation129130**Candidate classification**:131- `committee_decision: "Selected"` → selected candidate132- `committee_decision: "Waitlisted"` → waitlisted candidate133- `committee_decision: "Rejected"` → rejected candidate134135**Offer validation**:136- The selected candidate must have an entry in `offer_register` with `status: "accepted"`.137- `offer_id` and `offer_base_salary` come from the accepted offer.138139**Cost calculation**:140- `recruitment_cost_total` = **sum of all `amount` values** in `cost_ledger`.141- Source is always `recruitment_cost_ledger` (never case summary).142143**Notice follow-up**:144- Check `notice_packets` for each non-selected candidate.145- If `status: "not_sent"` and `required_action` says send → candidate ID goes in146 `notice_followup_required`.147- Waitlisted candidates with unsent notices → `send_waitlist_notice`.148- Rejected candidates with unsent notices → `send_rejection_notice`.149- Notice quality is determined from `notice_packet_inspection` (the notice_packets150 array in the recruitment record), not from messages alone.151152**Source hierarchy for recruitment**:153- Candidate status: `interview_feedback_and_offer` (from recruitment endpoint's154 candidates array and offer_register).155- Outcome control: `committee_decision_with_offer_confirmation`.156- Cost: `recruitment_cost_ledger`.157- Notice quality: `notice_packet_inspection`.158159### E. Policy Case Folder Readiness160161**Governing policy**: `POL-DOCS-2026` §5.1 — *"A folder is not ready unless all162required files and required tags shown in the folder checklist are present."*163164**Decision logic**:165- `folder_ready: true` ONLY when **all** `required_files` are present in `files` AND166 **all** `required_tags` are present in `tags`.167- Any missing file → `folder_ready: false`, list missing files in `missing_files`.168- Any missing tag → `required_tag_present: false`.169- Use the `/api/documents` endpoint to find the folder associated with the case.170- Also check case `attachments` of `kind: "Checklist"` — their `content` field171 confirms what is missing.172173### F. Formal Notice Quality174175**Notice inspection sources** (in priority order):1761. **Notice packet inspection** — the `notice_packets` array in `/api/recruitment`177 or the `messages` endpoint (`/api/messages`) for policy cases.1782. **Message notice inspection** — fallback when only messages are available.1793. **Case summary only** — lowest confidence; do not rely on this.180181**Quality determination**:182- A notice is `defective` when its `defects` array is non-empty.183- If `quality` field is explicitly set, use it; otherwise infer from `defects`.184- Defect types (from messages/notice_packets):185 - `missing_ack_deadline` — acknowledgement deadline absent186 - `missing_appeal_instructions` — appeal process not described187 - `missing_waitlist_status` — waitlist status omitted188 - `missing_correct_policy` — references wrong/legacy policy189190**Policy requirements** (from `HR-POL-014` §7.1):191International exception formal notices must include: executive approval reference,192time limits, tax equalization, VPN-only access, quarterly compliance review,193**appeal instructions**, and **acknowledgement deadline**.194195### G. Closeout / Final Control Gates196197**Approval closeout gate**:198- `approval_sufficient_when_records_clean` — all records are clean (no draft reliance,199 no folder defects, no notice defects).200- `approval_not_sufficient_when_folder_or_notice_defective` — any folder or notice201 defect blocks close.202203**Final control result**:204- `approve_closeout` — all checks passed, records clean.205- `hold_for_folder_and_notice_defects` — blocked by folder and/or notice issues.206- `ready_with_monitoring` — records are correct but profile/system state needs207 follow-up (e.g., stale profile summary, but assignment is clean).208209**Closeout blockers** (can be multiple):210- `missing_required_files` — folder missing required files.211- `missing_required_tags` — folder missing required tags.212- `defective_formal_notice` — formal notice has quality defects.213214### H. Cross-Module Escalation215216When a case like `XMODULE-77` references related audit events, inspect each217referenced event individually. Do not merge scopes:218- Leave-source audits → only for leave decisions.219- Document/notice audits → only for document/notice decisions.220- Payroll audits → only for payroll decisions.221222**Remediation ownership**:223- Missing files/tags → `Records`.224- Defective notices / policy issues → `People Ops Compliance`.225- Payroll assignment issues → `Payroll QA`.226227**Notice remediation actions**:228- `reissue_defective_notices` — when formal notice has defects.229- `send_new_offer_notice` — when offer notice needs to be sent.230- `no_notice_action` — when notices are clean.231232---233234## Audit Event Model235236### Event Types and Their Scope237238| Event | Scope | Use For |239|---|---|---|240| `leave.profile_mismatch` | `leave_source_precedence_only` | Leave source decisions |241| `payroll.ready` | `payroll_assignment_readiness` | Payroll readiness decisions |242| `payroll.draft_excluded` | `payroll_assignment_readiness` | Payroll draft exclusion |243| `notice.defect` | `document_notice_findings_only` | Notice quality decisions |244| `case.close_blocked` | `document_notice_findings_only` | Folder/notice blocking decisions |245| `folder.tag_missing` | `document_notice_findings_only` | Document/folder decisions |246| `cross_module.escalation_package` | cross-module (inspect each ref separately) | Escalation triage |247248### Supporting vs Excluded Audit Events249250When making a scoped decision:251- **Supporting**: Audit events whose scope matches the decision being made.252- **Excluded**: Audit events whose scope does NOT match (e.g., exclude253 `folder.tag_missing` events from a `leave_source_precedence_only` decision).254255---256257## Output Field Conventions258259### Normalized Enum Values260261**Precedence / source fields**:262- `approved_assignment_over_profile` — approved ledger assignment overrides stale profile.263- `employee_profile_summary` — only when no ledger assignment exists.264- `case_summary_only` — least authoritative; use only when nothing else is available.265- `approved_assignment_current_period` — the approved assignment for the current period.266- `profile_summary_current_period` — profile summary for the current period.267- `leave_assignment_history` — leave data sourced from ledger leave assignments.268269**Payroll source status**:270- `submitted` — authoritative submitted assignment.271- `draft` — excluded planning record.272- `superseded` — replaced by a later record.273274**Draft exclusion rule**:275- `exclude_draft_assignment` — draft records are always excluded.276- `draft_allowed` — drafts are permitted (rare; almost never used).277- `exclude_superseded_only` — only superseded records excluded.278279**Audit scope** (exactly one per decision):280- `leave_source_precedence_only`281- `document_notice_findings_only`282- `payroll_assignment_readiness`283284**Closeout / control actions**:285- `approve_onboarding_close` — proceed with close.286- `block_close_and_reissue_notice` — blocked; reissue notice.287- `open_records_remediation` — open remediation workflow.288- `update_employee_summary` — update stale profile.289- `no_action` — nothing needed.290291**Recruitment-specific**:292- `interview_feedback_and_offer` — candidate outcome from interview + offer data.293- `committee_decision_with_offer_confirmation` — committee outcome confirmed by offer.294- `notice_packet_inspection` — notice quality from notice_packets array.295- `message_notice_inspection` — notice quality from messages endpoint.296- `recruitment_cost_ledger` — costs from cost_ledger array.297- `case_summary_only` — fallback; least reliable.298299**Handoff / assignment gates**:300- `create_submitted_assignment_after_acceptance` — create submitted assignment once offer accepted.301- `create_payroll_precheck` — precheck only (not a full handoff).302- `no_payroll_handoff` — no handoff needed.303- `accepted_offer_only` — only accepted-offer candidates for payroll.304- `accepted_offer_and_submitted_assignment` — both conditions required.305- `all_interviewed_candidates` — all interviewed (rare).306- `submitted_after_acceptance` — submitted assignment required after acceptance.307- `submitted_handoff_required_after_acceptance` — handoff must be submitted after acceptance.308- `submitted_handoff_required` — submitted handoff generally required.309- `no_handoff_required` — no handoff needed.310311**Follow-up actions (recruitment)**:312- `send_waitlist_notice` — send waitlist notification.313- `send_rejection_notice` — send rejection notification.314- `reissue_waitlist_notice_not_rejection` — reissue notice without changing to rejection.315- `reissue_rejection_notice` — reissue rejection notice.316- `no_action` — no follow-up needed.317318**Offer exclusion reasons**:319- `no_accepted_status_or_offer` — no accepted offer exists.320- `waitlisted_not_selected` — candidate was waitlisted, not selected.321- `already_rejected` — candidate already rejected.322323---324325## Date, Calculation, and Sorting Rules326327### Dates328- All dates are in ISO 8601 format: `YYYY-MM-DD` for date-only, with `T` separator329 for timestamps (e.g., `2026-03-01T09:00`).330- `effective_date` for payroll assignments: use the `updated_at` date portion331 (e.g., `2026-04-01` from `2026-04-01T09:30`).332- For leave assignments, the controlling period is `2026` — filter by `period: "2026"`333 or the start of the period year.334335### Sorting / "Latest"336- When multiple assignments exist, the **latest** is determined by `updated_at`337 timestamp (not `ledger_id` order).338- For leave: pick the most recent `Approved` by `updated_at` for the current period.339- For payroll: pick the `Submitted` record (there is typically one).340341### Calculations342- **Recruitment cost**: Sum all `amount` values in `cost_ledger`. Use exact arithmetic;343 do not round.344- **Annual leave days / balance days**: Use `approved_leave_days` from the authoritative345 leave assignment. Do NOT use `worksheet_leave_days` from the ledger (that is a346 different field for worksheet purposes).347- For salary assignments, `approved_leave_days` and `worksheet_leave_days` are `0` —348 ignore them; use `base_salary`.349350### Candidate Arrays351- Arrays for `waitlisted_candidates`, `rejected_candidates`, and352 `notice_followup_required` must contain **candidate IDs only** (e.g., `"CAND-DA-7702"`),353 not names.354355---356357## Source Precedence (Cross-Module)358359When multiple data sources conflict, resolve in this order:360361### For Leave Entitlement3621. Approved leave assignment in payroll ledger (highest)3632. Submitted leave assignment in payroll ledger3643. Employee profile summary (lowest; stale when ledger exists)365366### For Base Salary3671. Submitted salary assignment in payroll ledger (highest)3682. Employee profile salary_band (descriptive only; not a dollar amount)3693. Draft salary assignment (excluded)370371### For Candidate Outcomes3721. Recruitment endpoint: `candidates[].committee_decision` + `offer_register[].status`373 (highest)3742. Audit events referencing the recruitment3753. Case summary (lowest)376377### For Notice Quality3781. `notice_packets` in recruitment, or `messages` endpoint for policy cases (highest)3792. Audit events on notice defects (corroborating)3803. Case summary (lowest)381382### For Folder Readiness3831. `/api/documents` folder record (highest)3842. Case attachment of kind `"Checklist"` (corroborating)3853. Case summary (lowest)386387### Evidence Source Order (for case review)3881. `approval_history_folder_notice_audit` — full chain: approvals → folder → notice → audit.3892. `folder_notice_audit` — folder → notice → audit (no approvals).3903. `audit_only` — audit events only.391392---393394## Common Pitfalls3953961. **Draft contamination**: Always filter out `Draft` status records from both leave397 and payroll ledgers. Draft records are planning placeholders and have no398 authoritative weight. This is the single most common error.3994002. **Profile staleness**: Employee profile `leave_balance_days` may not match the401 approved assignment. When a ledger assignment exists with a different value, the402 profile is stale. Set `profile_policy_ignored: true` and use the ledger value.4034043. **Scope mixing**: Do not use document/notice audit events (e.g., `folder.tag_missing`,405 `notice.defect`) to support leave-source-precedence decisions, or vice versa. Each406 audit event has exactly one scope. `supporting_audit_event_ids` and407 `excluded_audit_event_ids` must respect this boundary.4084094. **Superseded vs Draft confusion**: `Superseded` means a record was replaced by a410 newer one — it was once valid but is now obsolete. `Draft` means it was never valid.411 Both are excluded from current decisions, but they are semantically different.412 Only `Superseded` goes in `exclude_superseded_only` lists.4134145. **Cost sum precision**: Sum all cost_ledger amounts exactly as stored. Do not415 estimate, round, or omit line items.4164176. **Tag vs file confusion**: `folder_ready` requires BOTH all `required_files` AND all418 `required_tags`. A folder with all files but a missing tag is NOT ready. Check both419 independently.4204217. **Waitlist notice reissue vs rejection**: When a waitlisted candidate's notice is422 defective, the action is `reissue_waitlist_notice_not_rejection` — reissue the423 waitlist notice without converting it to a rejection. Do not confuse this with424 `send_rejection_notice` (for rejected candidates).4254268. **Recruitment notice packets vs messages**: For recruitment tasks, notice quality427 comes from `notice_packets` in the recruitment endpoint, NOT from the general428 `/api/messages` endpoint (unless the notice_packets reference a specific429 `message_id`). For policy cases, notice quality comes from `/api/messages`.4304319. **Payroll handoff requires accepted offer**: A candidate who is "Selected" but432 whose offer is still `draft` or absent does NOT trigger payroll handoff. The offer433 must be `accepted`.43443510. **Audit detail vs audit list**: `/api/audit/<event_id>` returns the same fields436 as the entry in `/api/audit` or in a case's `audit_events`. The detail endpoint437 is useful for confirming an event exists and reading its full detail field.43843911. **Employee ID in case vs employee ID in task**: Cases like `CASE-118` have440 `employee_id: "EMP-118"` (the subject employee). But audit events attached to the441 case may reference different entities. Always match on the task's target employee.44244312. **Folder document lookup**: A case may reference a folder in its attachments444 (kind: "Checklist"), but the authoritative folder record is in `/api/documents`.445 Cross-reference both — the document endpoint provides `required_files`,446 `required_tags`, `files`, `tags`, and `ready`; the attachment provides a human447 summary confirming what is missing.44844913. **Multiple assignments for same employee**: An employee may have multiple leave450 assignments or salary assignments. Always select based on status precedence and451 recency, not by picking the first one returned.45245314. **Policy reference without case detail**: When a case lists `policy_refs`, fetch454 each policy at `/api/policies/<policy_id>` to read the section text. The policy455 body text contains the authoritative business rule.45645715. **`worksheet_leave_days` vs `approved_leave_days`**: In leave assignment records,458 use `approved_leave_days` for the employee's leave entitlement. The459 `worksheet_leave_days` field is a separate worksheet tracking value and should not460 be used for entitlement decisions.