Fair Shift checks an hourly worker's pay in Taiwan or New York State against the official rules in force on the shift dates.

You paste a schedule (a LINE message from a manager works) and a payslip. An NVIDIA Nemotron agent on Nebius Token Factory reads the official pages through Tavily and records each rule with a quote that code checks word for word, and then code does the math.

I tested it on 22 checks taken from 11 court decisions in Taiwan and New York, comparing Fair Shift with the amount the court found. On its first run, before any fix, Fair Shift matched within NT$1 or US$0.01 in 7. After I fixed the bugs that run exposed, a rerun on the deployed code matched 9 (Taiwan 7 of 14, New York 2 of 8). The same Nemotron model answering alone matched 2. New York is the weak spot: in all 6 New York checks that differ, Fair Shift is above the court, by $12 to $374, and the reasons are written out below.

Video: https://youtu.be/8RiWYUsgaNw

Who pays

Nobody pays yet, and I would keep it free for workers. The people who could pay are the ones who already answer these questions or want to avoid them. Student unions, labor NGOs and city labor bureaus field pay questions from part-timers, and a check costs about one US cent in model calls (US$0.0114 on average in the 40-case eval), so a union could offer it to every member. Small employers and payroll services could run the same checks on their own schedules and payslips before an inspector does: in Taiwan's 2025 inspection of businesses that often hire students and part-timers, 465 of 1,852 broke labor law, and the most common violation was not paying double for holiday work (134 cases). The ministry picked those businesses and put earlier violators first, so the 25.11% is not a random sample.

What it does

Paste a schedule in whatever form you have it: a LINE message with the week in a header line and only weekdays below, your own notes, 18-22, 18時-22時, 下午5點半到10點, two shifts on one line, a night shift across midnight, or a texted English schedule. Add the payslip. The page then shows:

  • a conditional headline, for example "Based on what you entered, you may be owed up to NT$1,568". It says "up to" when the payslip shows only take-home pay, and in that case a gap under 5% of the expected pay is shown as possibly just deductions;
  • a bar that splits the expected pay into pay at your rate and each extra the rules add, against what the payslip shows;
  • what the number assumes, with warnings first: no break written on a long shift, a line it could not read, payslip hours that do not match the schedule, net pay instead of gross, a minimum wage that is announced but not yet formally in force;
  • two readings side by side when the input allows both, such as a national holiday that may have been your rest day, a shift with or without the usual break, or a New York tipped worker's gross that may include tips;
  • the reasons with article numbers, and every rule used with its exact quote, link and how the page was retrieved (read with Tavily extract, or found by Tavily search);
  • what to do next. For Taiwan that is the 1955 labor hotline, the local labor bureau, your right to a copy of your attendance record, protection from retaliation and the time limit for claims. For New York it is the NYS DOL Labor Standards complaint (form LS223), which the DOL takes for wages from the last three years; that is the DOL's window, and a claim in court has its own time limit;
  • a one-page printable report, and a message to the employer that cites the articles and says what the amount assumes (for example "I counted Oct 10 as work on a national holiday; if that day was my rest day, the rest day rules apply instead" and "my schedule does not list a break for Oct 10"), in Chinese with an English translation for Taiwan.

It stops and asks when a guess would be wrong. If no shift can be read, or the payslip has a monthly salary and no hourly rate, the page names the box with the problem, shows no amount and runs no agent.

Coverage: Taiwan hourly minimum wage by date (past rates from the Ministry of Labor's history pages, and the decided 2027 rate), daily and weekly overtime, rest day work, national holidays including lunar holidays for 2026 and 2027, and the regular day off. New York State minimum wage by region (including past rates), weekly overtime, tip credit and tipped overtime, and spread of hours. The page opens in English, switches to Traditional Chinese for a Chinese browser, and streams every agent step while it runs.

How it works, from request to result

  1. Redact (code). Names, ID numbers, phones, emails, account and card numbers, tax IDs, shop names and @handles are removed before any API call. Fair Shift keeps no copy of what you paste: no database, and request bodies are not logged. What is kept elsewhere: the per-visitor limits count checks by IP in memory for up to a day, the host keeps its standard request logs (time, path, IP), and Nebius Token Factory (which receives the redacted text) and Tavily (which receives only official URLs and fixed search words) may keep request data under their own privacy policies.
  2. Parse (Nemotron 3 Nano and code). Nano turns the text into shifts and payslip numbers in JSON. A rule-based parser reads the same text on its own. If the two disagree, or a line with a time cannot be read, the result is marked "read with low confidence" and the unread lines are listed. Weekday lines under a week written in the schedule are dated by code from that week, and a written break is kept even if the model drops it.
  3. Stop and ask (code), as described above.
  4. Retrieve (Tavily). While Nano reads, Tavily extract fetches the official seed pages: single articles of the Labor Standards Act from the Ministry of Labor's law database, the ministry's minimum wage pages, the government office calendar, the NYS DOL minimum wage pages and the hospitality wage order PDF. One Tavily search per check, restricted to official domains, looks for anything newer than the seeds. For a 2027 shift it finds the Executive Yuan's release of 2026-10-01 on the new minimum wage. Search finds pages; extract reads them; code checks the domain of every URL again.
  5. Find rules (Nemotron 3 Super, two tool-calling loops in parallel). One loop records dated amounts and Taiwan holidays, the other the multipliers and hour limits. Each rule goes in through a record_rules tool with an exact quote. Code accepts it only if the quote appears in text retrieved during this check and code can read the same value back out of it: the amount, the fraction (加給三分之一 means 4/3), the effective date, and for New York the region and job from the headings above the quoted row. A rejected entry goes back to the model with the reason. A loop ends as soon as its group is complete, and no model turn starts after 40 seconds.
  6. Fill gaps (code). If a loop stops early, code reads the missing rules from the pages already retrieved, through the same checks. Reports count agent records and these fallback records separately.
  7. Compute (code). Pay per shift and segment, minimum wage top-up, premiums, tip credit, spread of hours and the shortfall. Taiwan shifts across midnight are split at 0:00 because holidays are calendar days. The model never computes money.
  8. Explain (Nemotron 3 Ultra and code). Ultra writes a short explanation in English and Chinese. Code rejects it if it uses a number that is not in the calculation, simplified Chinese characters, or the word overtime when no overtime rule applies, and then uses a template. The message to the employer is always a code template, and it states what the amount assumes right after the amount.
Model on Nebius Token Factory Used for USD per 1M tokens in / out (catalog, 2026-10-13)
nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B parsing, JSON, reasoning on 0.06 / 0.24
nvidia/nemotron-3-super-120b-a12b the rule agent, two parallel loops with tool calls 0.30 / 0.90
nvidia/Nemotron-3-Ultra-550b-a55b the plain-language explanation, thinking off 1.00 / 3.00

The public demo has caps so it stays up through judging: 6 checks per visitor per hour and 20 per day, 60 checks and US$1.50 of model spend per day per server instance, 32 Tavily credits per day per instance, and a stop at 1,000 Tavily credits a month across instances. When a cap is reached, the page samples show the live result saved from the latest eval run, labelled as saved, and your own input gets a "try again later" message.

Results on 40 synthetic cases

npm run eval runs every case three ways: Fair Shift; the same Nemotron 3 Super agent with the same Tavily tools but with the quote checks and the code fallback turned off; and Nemotron 3 Super alone with today's date and the same text, one prompt, no tools.

40 synthetic cases Fair Shift Same agent, quote checks off Same model alone
Expected pay exactly right 40/40 36/40 6/40
Underpaid amount exactly right 40/40 n/a 8/40
Used the minimum wage in force on the shift dates (39 cases with an hourly rate) 39/39 n/a 5/39
Showed exactly the warnings the input needed (11 cases) 11/11 n/a n/a
Cited sources that contain the number used 304 quotes, all checked word for word not checked 0 of 95 links
Average model cost per check US$0.0114 US$0.0154 US$0.0028

The cases are 13 clean ones, 5 messy formats, 6 held out (committed with their expected values before Fair Shift ran on them), 5 shaped like inputs an earlier version got wrong, and 11 for inputs an internal review found handled wrongly or silently. The last two groups were committed with their expected values before the code that handles them. I ran this eval again on the deployed code (commit a4386f6) after the last fixes, so these numbers describe the version you can try. The agent made 271 of the 278 rule and holiday records itself (97%), and in 35 of 40 cases it recorded everything; code filled the other 7 from pages already retrieved, through the same checks, in three Taiwan holiday cases and two New York tipped-worker cases where the agent reached its 40-second limit. The median check took 16.8 seconds and the slowest 46.7 (one of those two New York cases). Tavily search ran 39 times and changed a fact or added a page in 3 cases, the three 2027 Taiwan cases. With the quote checks off, the agent missed 4: the Mid-Autumn Festival 2026 case and three New York cases (an old upstate rate and two tipped workers), where its own unchecked values were wrong. The whole run, all three columns, cost about US$1.18 in Nemotron calls and 11 Tavily credits.

Real-world check: court figures and public posts

I wrote the 40 cases and computed their answers myself, so I also tested against amounts that courts found. I took 14 checks from 6 Taiwan labor judgments about hourly or daily-paid workers and 8 checks from 5 New York federal wage decisions, reduced each court's facts to a schedule and a payslip, and compared only what Fair Shift computes (hourly pay, minimum wage, overtime, rest day, holiday and spread-of-hours pay). Severance, leave, penalties and interest are left out. The expected values were committed to the public repo before Fair Shift ran on any of them (commit 3e32d21); the fixes came afterwards in their own commits (2cc3b7c, and 8d08beb for weekday dates). "First run" is the frozen run before any fix; "rerun" is the same set run again on the deployed code (a4386f6).

Agreement with the amount the court found (22 checks) Fair Shift, first run Fair Shift, rerun after fixes Same model alone
Exact 7 7 2
Exact or within NT$1 / US$0.01 7 9 2
Within 5% (includes the rows above) 12 13 3
Off by more than 5% 9 8 19
No amount (stopped and asked) 1 1 0

By place, within NT$1 or US$0.01: Taiwan 2 of 14 on the first run and 7 of 14 in the rerun; New York 5 of 8 on the first run and 2 of 8 in the rerun. The New York drop needs saying plainly. Four of the five New York matches on the first run were right for the wrong reason: Fair Shift had not found the minimum wage at all, and that also dropped the spread-of-hours pay. With the minimum wage found, they differ:

  • In New York, all 6 checks that differ are above the court, by $12 to $374.
  • Two are spread-of-hours pay (one extra hour at the minimum wage on a day longer than 10 hours) that the workers gave up in court.
  • One is a court that paid only the half-time premium on overtime hours.
  • Two are spread of hours for workers outside restaurants who were paid above the minimum wage. This has a second, disputed reading. Fair Shift owes the extra hour only to the extent pay above the minimum wage does not already cover it (12 NYCRR 142-2.4, "in addition to the minimum wage required"), which gives $12 and $31.50 here; the court (E.D.N.Y. 1:25-cv-03219) gave nothing to anyone paid above the minimum wage. Until that is settled, a New York spread-of-hours amount for someone paid above the minimum wage is one reading, not the answer.
  • One is a misread schedule. An earlier run right after the fixes matched this check exactly (10 of 22 overall then). In the rerun, Nemotron Nano read a seventh shift that is not in the schedule; the rule-based cross-check disagreed, so the page marked the reading low confidence and warned that the payslip's 86 hours do not match the hours read. Wrong, but flagged: the model's reading varies between runs, and those warnings are the safeguard.

The first run found real bugs. Fair Shift had no minimum wage for any shift before 2026 and still showed a number, it paid 2023-09-28 as Teachers' Day (a day off only since the 2025 amendment), and it rounded hours to 0.01 before multiplying. It now also reads the minimum wage history pages of the Ministry of Labor and NYS DOL, and a missing rate is flagged next to the number.

Each of the 13 checks that are not within NT$1 or US$0.01 in the rerun has a written reason: the worker claimed less than the law gives (3), the court rounded 4/3 to 1.34 (3), a rule that can be read two ways (3), a court table that does not add up (1), the court's own formula (1), the misread schedule above (1), and one case with no agreed rate, where Fair Shift asks instead of assuming the minimum wage (1). Besides spread of hours, one reading matters to Taiwan users: for an hourly worker's rest day, one Taiwan court added 1 1/3 on top of the hourly pay, while Fair Shift follows the Ministry of Labor and uses 1 1/3 as the total.

I also ran 24 public PTT posts (workers' questions and part-time job ads) through the parser. Fair Shift read the shifts right, or invented none where none could be read, in 21, on both runs. The other 3 were wrong or missing and the result said so; none was wrong without a warning. The same model alone also got 21 right, and its 3 misses came with no warning. The rerun cost about US$0.62 in Nemotron calls and 10.6 Tavily credits.

The sample is small and I found it by keyword search: 34 of the 40 Taiwan judgments and 5 of the 10 New York decisions I opened were not usable. Courts give hours rather than clock times, so I chose start times, and a court figure is what was claimed and proved, which can be less than the law allows. Cases, sources and reasons are in eval/realworld/.

Challenges I ran into

The first version of the agent hardly did the work. Super fetched one page per turn and hit the step limit, and code filled 73 of 93 rule records. Three changes fixed it: one fetch_pages call for all seed pages, one record_rules call for all rules, and seed pages of about 1,000 characters (one law article per page on the Ministry of Labor's law database) instead of the whole act. In the latest eval the agent made 271 of 278 records.

Speed came from running things side by side. The seed pages are fetched while Nano parses, the two Super loops run in parallel, and a loop ends as soon as its rules are recorded. Turning reasoning off made each turn faster, but the model then needed many more turns, so reasoning stays on.

Real inputs failed quietly. A monthly-salary payslip, a payslip that says 實領, or a LINE schedule with the week in the header made an earlier version guess. An internal review then typed more of them and found quieter problems: a 9-to-6 shift with no break written was counted as overtime without a word, the hours after midnight on National Day were paid as a normal day, a second shift on the same line disappeared, and 18時-22時 vanished without lowering the confidence. Each of these got an eval case with a frozen expected value before the fix.

While I recorded the demo video, one live run of the LINE sample got a Nano reading that put the Saturday National Day shift on Friday. The result was flagged as low confidence but showed no underpayment. The rule-based parser dates weekday lines under a written week by calendar arithmetic, so its date now wins, and the step list shows a date_from_week step when that happens (commit 8d08beb, with a test that replays the misdated reading).

A last review found two things in the result page that a worker could take the wrong way. The message to the employer said 應領 NT$4,606 (expected NT$4,606) without saying that the number assumed National Day was not her rest day and that the 8-hour shift had no break. And Ultra's plain summary called the holiday double pay "overtime", because my prompt told it to say "the overtime assumes no break" whenever no break was written. The message now states its assumptions right after the amount, the prompt names each reason by its kind, and code rejects a summary that says overtime when no overtime rule applies (commit b9d3a04). I then reran both live evals on that code, which is where the numbers above come from.

Rejections have to say how to fix the quote. The model once quoted the right sentence with the wrong source URL, so the check now says which retrieved page the sentence is on. JavaScript's built-in Chinese calendar puts lunar new year 2027 on February 7, while the official Taiwan government calendar says February 6, so the dates are a table copied from the official calendars and pinned by a test. In New York, the tip credit row "$11.35 Cash Wage, $5.65 Credit" appears under several regions, so code checks the heading above the quoted row.

Limits

  • Taiwan and New York State only, hourly pay only. It does not check flexible schedules (變形工時), the monthly overtime cap, monthly-paid jobs, Art. 84-1 jobs, or New York call-in pay; the result page lists these.
  • I wrote the 40 cases and the fixes, so 40 of 40 shows that the problems those cases describe are fixed. It does not show that every real schedule will be read right.
  • The court sample is small, and a court figure can be less than the law gives.
  • No real worker has used the tool yet. A round with part-time workers pasting their own schedules is the next step.
  • It is a checking aid, not legal advice. Every rule it uses is shown with its source so a worker or a labor bureau can verify it.
  • The privacy filter can miss a name, so the page asks people to cover names before pasting. All samples on the page are synthetic.

Sources

How to test it

  1. Open https://fair-shift-nu.vercel.app. No login. The badge at the top right should say "Live".
  2. Click "Try a sample payslip". After 15 to 30 seconds you should see "you may be owed NT$1,176" for a synthetic Taipei clerk who worked National Day at straight time. Scroll down for the two readings, the rules with their quotes, and what to do next. "What the agent did" lists every step.
  3. Under "More samples", pick "LINE message: date range in the header, weekdays per line, worked National Day" and click "Check my pay".
  4. Pick "January 2027 shifts at the 2026 rate". The minimum wage card should say it was announced by the Executive Yuan on 2026-10-01 with the formal notice pending, and the "Freshness check (Tavily search)" box under "Official rules used" shows the search and the page it found.
  5. Switch to "New York" and pick a sample, for example the NYC barista with 42.5 hours, to see NYS DOL next steps.
  6. Paste your own schedule if you like. Names are removed before any API call, and Fair Shift keeps no copy of what you paste.
  7. If a daily cap has been reached, samples show the saved live result with a "Saved result" label.
  8. Code: npm install, then npm test runs 217 unit, pipeline and API tests offline. Without API keys the app runs in a labelled demo mode on saved copies of the official pages. npm run eval and npm run eval:realworld need NEBIUS_API_KEY and TAVILY_API_KEY.

Built during the hackathon

All of it. I started the project on 2026-10-13, inside the submission period.

Built With

Share this project:

Updates

Submission history