← Daily

Problem breakdown · No. 016 · October 31, 2026

Makeup lessons leak money when the credit ledger lives in chat

Small tutoring centres promise makeup lessons for missed sessions, then track those promises across chat threads and a spreadsheet. The fix is a boring ledger with a narrow language layer on top, not a scheduling chatbot.

The problem

A tutoring centre with a few rooms and a handful of part-time tutors usually has a makeup policy: cancel with enough notice and the student gets a credit to use within some window. The policy is simple. Running it is not.

Cancellations arrive as chat messages, calls and notes passed on at pickup. The owner or a single admin turns them into spreadsheet rows when there is time. Credits get missed, applied twice or forgotten until a parent asks why they paid for a lesson their child never had. Meanwhile seats sit empty in sessions that could have absorbed those makeups.

The cost is threefold: refunds and goodwill given away during disputes, unused tutor hours, and admin time spent reconciling. Existing class management tools handle bookings and invoices well, but makeup rules vary by centre, and the input that matters is unstructured messages, which those tools never see.

Constraints

  • Budget: a small monthly subscription at most, and no appetite for a migration project.
  • Skills: the operator is an educator, not a technical person. Anything that needs configuration beyond a form will not be used.
  • Data: the roster and timetable often live in a spreadsheet or an export from an existing tool. Messages live in a business chat account.
  • Regulation: the data concerns minors. Retention should be short, access narrow, and nothing should be sent to a model that the centre has not agreed to.
  • Trust: parents dispute credits. Every balance has to be explainable line by line, with the message it came from.

System design

The core is an append-only credit ledger. Each entry is an event: session_missed, credit_granted, credit_redeemed, credit_expired, each linked to a student, a session and a source message. Balances are derived, never edited.

Policy is deterministic code driven by a small config: notice window, expiry period, which subjects and levels are interchangeable. Given a cancellation timestamp and a session, the rule engine decides eligibility. No model touches this decision.

The slot finder is also plain code. It filters future sessions by subject, level, room capacity and tutor, and returns ranked options.

The LLM belongs at the edge, in two places:

  • Parsing inbound messages into a structured intent: student, session date, cancel or reschedule request, preferred times. Output is validated against the roster; unknown names or ambiguous dates go to a review queue instead of being guessed.
  • Drafting the reply to the parent from the ledger's facts and the slot finder's options. The draft cannot state a balance the ledger did not produce.

The admin approves drafts with one tap. Writes to the ledger are idempotent per source message, so a reprocessed message never grants a second credit.

Trade-offs

Keeping a human on every outbound reply slows response time. For a centre where a wrong credit costs a relationship, that delay is cheap.

The system does not replace the existing booking tool. It reads the timetable rather than owning it, which means double entry for redemptions until an integration exists. I prefer that to asking an owner to migrate.

The parser will fail on messy messages. Routing those to review, rather than tuning for coverage, keeps the ledger trustworthy while volume is low.

What I would build first

A spreadsheet import for roster and timetable, the ledger with the rule engine, and a review screen where the admin pastes or forwards cancellation messages. The model extracts the intent, the rules decide the credit, and the screen shows the balance with its evidence.

Run it alongside the current spreadsheet for two weeks. If it catches credits the spreadsheet missed or settles a dispute in seconds, chat integration and drafted replies are worth adding next.

Let the model turn messages into structured claims, and let a deterministic ledger decide what those claims are worth.