On this page (14)
- 1. Template assignment
- 2. Notice tier
- 3. Addendum assignment
- Addendum 1 — Drivers
- Addendum 2 — Bakers (Senior/Head)
- Addendum 3 — Young Workers
- Director's Service Letter
- 4. Pay phrasing
- 5. Merge field sources
- 6. Edge cases requiring human decision
- 7. Updating these rules
- 8. Templating engine
- 9. Placeholder marker convention
- Change log
Contract Derivation Rules
Purpose: rules the generator (when built) uses to decide, for each employee in Employee Contract Info - Wild.csv:
- Which template (A / B / C)?
- Which notice tier (Standard / Senior / Director)?
- Which addendums (1 Drivers / 2 Bakers / 3 Young Workers)?
- Whether the Director's Service Letter attaches.
- What merge field values populate from the CSV.
Reproducibility: any future regeneration should produce the same template/addendum assignments unless the CSV changes or rules are updated here.
1. Template assignment
Apply rules in order. First match wins.
| Rule | Condition | Result |
|---|---|---|
| 1 | Job Title contains "Consultant" |
Template C — Consultancy |
| 2 | Job Title contains "Evening Packer", "Weekend Cleaner", "Visiting", "Stage" |
Template B — Casual |
| 3 | Team(s) contains "Evening Packing" OR "Weekend Cleaning" |
Template B — Casual |
| 4 | Hours Per Week = 0 AND Salary is blank |
Flag as incomplete — do not generate (Harry Reith case) |
| 5 | Otherwise | Template A — Employment |
Manual overrides currently applied (cases where the data is ambiguous):
- Caledonia Jeffrey: kept on Template-A with 9 guaranteed hours/week. The Drive analysis (2026-05-17) showed her current "casual" contract is structurally a full employment contract with guaranteed hours; moving her to Template-B would be a downward variation of existing rights. Overridden in overrides.yaml.
- Cameron Simpson, Rebekah France: PT cleaners on Weekend Cleaning team → B (confirm intent — flip to A if they want guaranteed hours)
- David Johnson: 0h, "Consultant" title → C
2. Notice tier
Determines the post-probation notice period in clause 11.2 of Template A.
| Tier | Notice (either side) | Trigger condition |
|---|---|---|
| Director | 6 months | Job Title = "Director" OR person is named on Companies House as a current director |
| Senior | 2 months | Job Title is one of: "Head Baker", "Senior Baker", "Head Driver", "Assistant Operations Manager", "Operations Manager", "Shop Manager" |
| Standard | 1 month | All other Template A staff |
| N/A | (no notice required) | Template B (casual) and Template C (consultancy — uses contract-specific notice) |
Statutory minimum floor applies for staff with 2+ years' continuous service (employer side only): 1 week per complete year, capped at 12 weeks.
3. Addendum assignment
A single employee can receive multiple addendums.
Addendum 1 — Drivers
Attaches when any of these is true:
Job Titlecontains "Driver"Team(s)contains "Drivers"- Manual flag (set per employee for borderline cases — e.g. Sean Mackenzie may need this depending on whether he drives in practice)
Addendum 2 — Bakers (Senior/Head)
Attaches when Job Title is one of:
- "Head Baker"
- "Senior Baker"
Does not attach to "Baker" or "Trainee Baker" — those roles don't have recipe-level access.
Addendum 3 — Young Workers
Attaches when, at the start date of the contract being issued:
- Age in years (
current_date−Date of Birth) is < 18
Detaches automatically when the worker turns 18 (and main contract terms become fully effective).
Director's Service Letter
Attaches when person is a statutory director (verified against Companies House register).
4. Pay phrasing
| BrightHR "Rate" | Contract phrasing |
|---|---|
Hourly |
"£{{rate}} per hour, paid monthly by BACS in arrears" |
Monthly |
"£{{rate × 12}} per annum, paid in 12 equal monthly instalments" |
Annually |
"£{{rate}} per annum, paid in 12 equal monthly instalments" |
For casual workers (Template B), always "£{{rate}} per hour, plus accrued holiday pay at 12.07% of hours worked, shown on each payslip" regardless of CSV basis.
For consultants (Template C), CSV basis is treated as the payment cadence of the retainer/fee, not annualised.
5. Merge field sources
| Variable | Template | Source |
|---|---|---|
{{first_name}} |
All | CSV: First Name |
{{last_name}} |
All | CSV: Last Name |
{{address}} |
All | CSV: concat Address Line 1 + Address Line 2 + Address Line 3 + Town/City + County + Postcode (skip blanks) |
{{dob}} |
Addendum 3 | CSV: Date of Birth |
{{start_date}} |
All | CSV: Start Date |
{{job_title}} |
A, B | CSV: Job Title |
{{reports_to}} |
A, B | CSV: Reports To (first name only if multiple) |
{{location}} |
A, B | CSV: Location |
{{hours_per_week}} |
A | CSV: Hours Per Week, with per-employee overrides applied from overrides.yaml |
{{pay_basis}} |
All | CSV: Rate (Hourly / Monthly / Annually) |
{{rate}} |
All | CSV: Salary (with pay-phrasing rule from §4) |
{{holiday_entitlement}} |
A | CSV: Holiday Entitlement |
{{notice_period}} |
A | Derived: see §2 |
{{pension_provider}} |
A | Per-employee variable — to be confirmed. Source TBC (likely BrightHR custom field or overrides.yaml). Until confirmed, generator should fail-fast on missing value rather than default |
{{employer_contribution}} |
A | CSV: Employer Contribution. Flag as missing if blank (do not silently default) |
{{employee_contribution}} |
A | CSV: Employee Contribution. Flag as missing if blank (do not silently default) |
{{annual_salary}} |
A | Computed: if pay_basis = Monthly → rate × 12; if Annually → rate; if Hourly → not used |
{{services_description}} |
C | Not in CSV — manual entry |
{{trading_name}} |
C | Not in CSV — manual entry |
{{rate_basis}} |
C | Not in CSV — manual entry |
{{director_appointment_date}} |
Director's letter | Companies House: 25 Aug 2016 (John) |
{{annual_hours}} |
A §4.8 | Per-employee — overrides.yaml annual_hours field. Only required when annualised_hours: true. Generator should fail-fast on missing when annualised flag is set, and not emit §4.8 when the flag is unset. |
{{notice_tier}} |
Cover-letter | Derived: same as §2 notice tier (Standard / Senior / Director). Used to drive the cover-letter's Senior-asymmetric-notice carve-out paragraph. |
{{addendum_drivers}} |
Cover-letter | Boolean. True when Addendum 1 attaches per §3 Drivers rules. |
{{addendum_bakers}} |
Cover-letter | Boolean. True when Addendum 2 attaches per §3 Senior/Head Baker rules. |
{{addendum_young}} |
Cover-letter | Boolean. True when Addendum 3 attaches per §3 (DOB < 18 at start). |
{{directors_letter}} |
Cover-letter | Boolean. True for statutory directors (per §3 Director's Service Letter rule). |
{{letter_signer}} |
Cover-letter | Default "John Castley". For John's own letter, override to "Tim Brown" (the Assistant Operations Manager on behalf of the Board — per cover-letter.md "Signer convention" note). |
| Company constants | All | Wild Hearth Bakery Ltd, SC543706, 42 Comrie Street registered office, 15A Cultybraggan Camp, Comrie, Crieff PH6 2AB principal place of business |
6. Edge cases requiring human decision
| Person | Issue | Default | Override note |
|---|---|---|---|
| John Castley | Pay vs NMW | Hours capped at 19.8/wk via overrides.yaml | Office-holder duties beyond 19.8h are unpaid director duties, not employment |
| Michael Hansen | Record incomplete | Hold contract until data collected | — |
| Harry Reith | 0 hours, no pay | Hold contract until pattern confirmed (A or B?) | — |
| Sean Mackenzie | Manages drivers, not on Drivers team | Default no Addendum 1 | Confirm whether he drives in practice |
| Bravo Nyamudoka | Excluded from this pass | Skip generation | Add own record to BrightHR before generation |
| Caledonia Jeffrey | PT in CSV but on Evening Packing team | Template B | Confirm with Caledonia |
| Cameron Simpson, Rebekah France | PT cleaners, regular hours | Template B | Confirm intent — Template A if they want guaranteed-hours contract |
7. Updating these rules
When the rules change:
- Update this document first.
- Update the generator script to match.
- Regenerate and review any contracts whose classification would change.
- Note the change with date in the section below.
8. Templating engine
Chosen: Jinja2.
Reasons:
- Supports conditionals (
{% if pay_basis == 'Hourly' %}...{% endif %}) needed in Template A §6.1 - Supports the section-style blocks used in
cover-letter.md - Standard Python library, well-documented
- Easy to add filters (e.g.
{{ rate | round(2) }},{{ pay_basis | lower }})
Generator should:
- Load CSV → dict per employee
- Apply derivation rules (§1–§3) to choose template + addendums
- Compute derived fields (§4
annual_salary; §2notice_period; etc.) - Apply per-employee overrides from overrides.yaml
- Render template using Jinja2
- Strip any
PLACEHOLDER...markers (see §9) - Emit one Markdown file per employee, plus a manifest
9. Placeholder marker convention
Anything in the form PLACEHOLDER...content... is a note for Zoe or internal review, not contract text. The generator must remove all PLACEHOLDER... blocks before issuing.
This applies across all template files. Examples currently in the pack:
- Template A §12.3 (training repayment placeholder)
Other in-doc notes that are binding contract text should not use this marker — e.g. Template C §1.5 is a real clause requiring CEST assessment, not a placeholder.
Change log
- 2026-05-17 — Initial rules drafted. Hours override for John Castley moved to
overrides.yaml. Pension provider set as per-employee variable (TBC source). Pension contributions no longer silently defaulted. Template C trigger simplified to Job Title contains "Consultant". Jinja2 chosen as templating engine.PLACEHOLDER...placeholder convention added.