MMichael Z Consulting
all cases
case 01 · Online education

EduSchedule

A conversational ops engine that resolves teacher absences end-to-end — substitute matching, reschedule, parent notifications.

PythonFastAPIClaude APITelegramSlackGmail
[demo video coming soon]
/ the problem

When a teacher calls out, a small ops team scrambles for 30-60 minutes per incident: find a qualified substitute across timezones, reschedule the affected sessions, message parents and students, and update the source-of-truth roster. The work is mechanical but high-stakes — one missed message is a refund.

/ the approach

A conversational gateway: ops messages a Telegram bot, the bot parses intent with Claude, runs a 5-step matching engine (G1–G5) over the roster, proposes a reassignment plan, and only acts after a `yes` confirmation. Side effects (Slack DMs to the sub, Gmail drafts to parents, sheet writes) are isolated behind connector Protocols so each one can be mocked, dry-run, or swapped without touching the engine.

/ the outcome

End-to-end demo handles three scenarios (happy path, sub rejection, unknown teacher) in dry-run mode with every side effect logged. 17 unit tests cover matching, workflow, NLU, and orchestrator. Designed so the live deploy is a config flag, not a rewrite.

/ engineering notes

Connector Protocols, not concrete platforms

The engine talks to DataStore / Notifier / ChatSurface Protocols. Concrete implementations (CSVDataStore, SheetsDataStore, GmailNotifier, SlackNotifier, TelegramChatSurface) plug in at the edge. Mock data and production data are the same code path — you flip a config flag.

Drop-in NLU swap

Intents are parsed by a `MockIntentParser` (regex, offline, deterministic for tests) or a `ClaudeIntentParser` (Anthropic SDK). Same interface, identical downstream behavior — chosen at boot.

State machine with JSON-backed per-chat state

Multi-turn orchestrator goes IDLE → PROPOSING → EXECUTING → IDLE. Crash-safe because state lives on disk, not in memory — bot can restart mid-conversation and pick up.

Confirmation gate before any side effect

The bot never writes, sends, or notifies without an explicit `yes`. The proposing step shows exactly what will happen; the executing step is fire-and-forget after confirmation.

Got a workflow that looks like this?

Run the free 5-minute diagnostic — I'll tell you whether it's a $0 weekend project or a 2-week build.