Skip to content

feat(young-persons): age at engagement, the section 3A Schedule bar, and section 7 hours the adult engine cannot express (#1877) - #1882

Merged
Dev1822 merged 1 commit into
Dev1822:mainfrom
MOHITKOURAV01:feat/1877-adolescent-employment
Aug 28, 2026
Merged

feat(young-persons): age at engagement, the section 3A Schedule bar, and section 7 hours the adult engine cannot express (#1877)#1882
Dev1822 merged 1 commit into
Dev1822:mainfrom
MOHITKOURAV01:feat/1877-adolescent-employment

Conversation

@MOHITKOURAV01

@MOHITKOURAV01MOHITKOURAV01 commented Aug 28, 2026

Copy link
Copy Markdown

Description

This PR implements the Child and Adolescent Labour (Prohibition and Regulation) Act, 1986 — the age at engagement, the section 3A Schedule bar, and the section 7 hours that workingHoursCompliance.js cannot express.

  • Engine (utils/adolescentEmployment.js): age classification as a function of a date, the two prohibitions kept separate, the section 7 spell/interval/day/night/overtime/weekly-day-off limits, overtimeTreatment as an explicit precedence rule, and assertNoAmounts as a guard.
  • Models (adolescentEmployment.model.js): AgeRecord (date of birth and what it rests on), YoungPersonRegister (the section 11 register), EmploymentFinding (no amount field), EmploymentAssessment (counts, with the Schedule snapshotted).
  • Controller & routes: /api/young-persons/rules, /age-records, /register, /register/:id/days, /assessment, /findings, /assessments.
  • Page (YoungPersonsRegister.jsx): prohibited engagements as a blocking banner, then the register, then the section 7 limits in force. No currency anywhere on it.
  • Tests: 56 unit tests.

The design point: there is no money in this feature

An underage engagement has no compensable amount. Section 14's punishment is imprisonment and a fine on conviction — a criminal penalty, not a liability that accrues. A rupee figure here would be summed into a compliance provision by the first report that read it, and the resulting line would state, in a number, that employing a child costs a known amount.

So:

  • every finding is an occurrence, a person, a date and a section;
  • assertNoAmounts walks any result for a money-shaped key and is exported for the suite;
  • GET /assessment runs it on the way out and returns 500 rather than a payload with a price in it;
  • EmploymentFinding has no amount, cost, penalty or exposure field, and EmploymentAssessment carries counts of people;
  • the page has no currency formatter imported.

Two prohibitions, not one

A child is anybody below fourteen and section 3's bar is total — any occupation or process, with only the family-enterprise and audio-visual-artist provisos. Both are claims about a relationship and about schooling rather than job titles, so an unevidenced claim is a finding and the register refuses (422) to record one. The family exception does not reach a hazardous occupation.

An adolescent is fourteen to under eighteen and section 3A's bar is by Schedule — mines, inflammable substances, explosives, and the hazardous processes incorporated by reference from section 2(cb) of the Factories Act. Outside the Schedule the engagement is permitted, under section 7.

Collapsing the two into one "underage" test would either bar lawful adolescent work or permit a child.

Classification is a date, and it moves

Somebody engaged lawfully as an adolescent turns eighteen during their employment and the section 7 limits fall away on that day. So classification is computed by classifyOn(date) and never persisted — assessPerson recomputes it per worked day, and a roster straddling the birthday is measured against these limits before it and against the adult engine's after. completedYears uses calendar arithmetic rather than days ÷ 365.25, because a leap-day birthday is exactly the case where a day is the whole question.

The six-hour day is inclusive

Section 7(2) caps the day including the interval and any waiting time. Six hours of work plus the one-hour interval the Act requires is a seven-hour day and is over the limit. So assessDay measures from first start to last end rather than summing the spells, and adds recorded waiting time.

Overtime is not a rate

workingHoursCompliance.js answers an excess hour by computing the section 59 double rate. Section 7(4) prohibits overtime for a young person outright. overtimeTreatment states that precedence in code and is carried on every person in the payload, so a caller cannot reach the wrong engine by default. It returns a sentence, never a figure.


Related Issue


Component(s) Affected

  • Backend (backend/)
  • Web app (frontend/)
  • Mobile app
  • Docs only
  • CI / tooling

Type of Change

  • New feature
  • Bug fix
  • Refactor
  • Other:

Testing Performed

  • 56 unit tests on utils/adolescentEmployment.js, all passing.
  • assertNoAmounts asserted to return [] over assessEstablishment, assessDay and overtimeTreatment results, and asserted to find a planted amount / penalty key so the guard itself is not vacuous.
  • Result asserted not to carry estimatedPenalty, exposure or totalLiability.
  • Classification asserted to change on the day (14 June vs 15 June for a 15 June birthday) and to handle a 29 February birthday without drifting.
  • The same long day asserted to be a DAY_EXCEEDS_LIMIT breach the day before somebody turns eighteen and not a finding the day after.
  • Six hours of work plus a one-hour interval asserted to be 420 minutes and over the ceiling.
  • Night work asserted for a start before 08:00, an end after 19:00, and a shift running past midnight.
  • Family-enterprise exception asserted to fail unevidenced, to fail where it interferes with schooling, and to fail in a Schedule occupation.
  • Prohibited findings asserted to be kept out of the regulated list.
  • permissions.routeCoverage passes for the new router (the unrelated failures in that suite are pre-existing on main).
  • ESLint and Prettier clean on all new and changed files.

Checklist

  • Rebased from latest main — zero merge conflicts
  • Clean and simple code with JSDoc comments
  • No secrets committed
  • No monetary field in any model, response, audit line or page in this feature

Merge behaviour

This PR touches five shared wiring files — the permission vocabulary, the audit vocabulary, app.js, the route-mounting test, and the frontend navigation registry. Its blocks are placed at their own anchor in each of those files rather than beside the most recently added neighbour, so that this and the sibling PRs in the same batch do not collide on the same hunk.

Verified by merging all of them onto main in forward and shuffled order: no conflicts either way, node --check clean on the combined result, all five routers mounted, and permissions.routeCoverage gaining its new cases with no new failures.

@vercel

vercelBot commented Aug 28, 2026

Copy link
Copy Markdown

@MOHITKOURAV01 is attempting to deploy a commit to the Dev's projects Team on Vercel.

A member of the Team first needs to authorize it.

…tion 7 hours the adult engine cannot express (Dev1822#1877)
The employee model carries a date of birth and nothing compares it against the
date of engagement. `workingHoursCompliance.js` — the module that would notice —
implements the adult Factories Act limits: nine hours a day, forty-eight a week,
the section 59 double rate. An adolescent rostered for nine hours passes every
check in this product today.
There is no currency anywhere in this feature, and that is the design rather
than an omission. An underage engagement has no compensable amount. Section 14's
punishment is imprisonment and a fine on conviction — a criminal penalty, not a
liability that accrues — and a rupee figure in this module's output would be
summed into a compliance provision by the first report that read it, stating in
a number that employing a child has a price. `assertNoAmounts` walks any result
for a money-shaped key, the assessment endpoint runs it on the way out and fails
with a 500 rather than returning one, and the test suite asserts it over every
shape the engine produces.
Two prohibitions rather than one. A child is anybody below fourteen and section
3's bar is total — any occupation or process, with only the family enterprise
and audio-visual artist provisos, both conditional on schooling and both claims
about a relationship rather than job titles, so an unevidenced claim is a
finding and the register refuses to record one. An adolescent is fourteen to
under eighteen and section 3A's bar is by Schedule. Collapsing the two into one
"underage" test would either bar lawful adolescent work or permit a child.
Classification is a function of a date and is never stored. Somebody engaged
lawfully as an adolescent turns eighteen during their employment and the section
7 limits fall away on that day — not at the end of the month and not when
somebody updates a field. `assessPerson` recomputes per day, so a roster
straddling the birthday is measured against these limits before it and against
the adult engine's after.
Section 7's day is six hours *including* the interval and any waiting time,
which is the part people misread: six hours of work plus the one-hour interval
the Act requires is a seven-hour day and is over. So the day is measured from
first start to last end rather than by adding the spells.
And overtime is not a rate here. `overtimeTreatment` states the precedence in
code — for anybody under eighteen the section 59 computation does not apply,
because there is no rate at which the hour becomes lawful. It returns a sentence
and never a figure.
@MOHITKOURAV01
MOHITKOURAV01force-pushed the feat/1877-adolescent-employment branch from 3931689 to 307f58aCompareAugust 28, 2026 16:47
@Dev1822Dev1822 added enhancement New feature or request ECSoC26 good-pr labels Aug 28, 2026
@Dev1822
Dev1822 merged commit 000c1f7 into Dev1822:mainAug 28, 2026
6 of 20 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Child and Adolescent Labour Act 1986: age at engagement, the Schedule bar, and section 7 hours the adult engine cannot express

2 participants

@MOHITKOURAV01@Dev1822