Case study
Semantask
Real-time work coordination
An open-source work-coordination platform. Teams talk in chat, conversation becomes reviewable suggestions, managers approve, and tool-running autonomy stays optional and policy-gated.
- 01Conversation
- 02Suggestion
- 03Approval
- 04Execution
Execution is optional. Tools run only when org policy allows; the default mode is suggest_only.
- Status
- Open source, MIT
- Demo
- None hosted — screenshots below
- Shape
- Turborepo: web, socket, task-worker
- Stack
- Node.js 24, TypeScript, MongoDB, Redis
01Product
What it looks like


Local build with seeded demo data — the names and messages are not real users.
02Problem
Why this is hard
Chat is where work gets agreed. Turning that conversation into tasks automatically is only useful if tools never fire on their own: in Semantask's suggest-first contract (ADR-005), a tool side effect nobody approved is a P0 bug.
Underneath, that becomes a systems problem. Chat has to stay real-time, extraction and execution have to run off the request path, and a crashed or duplicated worker must never repeat a side effect such as an email or a GitHub issue.
03Approach
The shape of the solution
- 01
Suggest-first: conversation becomes reviewable suggestions, approvals stay with humans, and organization policy decides whether tools may run at all.
- 02
Ingress classification calls an LLM by default and falls back to the regex classifier when the model is missing or fails; a confidence threshold decides what becomes a suggestion. Assignee and due-date candidates stay heuristic.
- 03
The chat stack it grew from stays: HTTP persists first, Socket.IO only transports, Redis coordinates rooms and ephemeral state.
- 04
Execution moves to a dedicated task worker fed by a transactional outbox, with DB leases, heartbeats, and idempotency at the event, action and tool layers.
04System
How the pieces fit
- apps/web
- Next.js UI, APIs, auth, onboarding and the /app workspace. The only writer.
- apps/socket
- Transport-only Socket.IO server. Schema-free.
- apps/task-worker
- Outbox consumer; runs the AgentRunner and tools only when policy allows.
- apps/mobile
- Optional Expo client (untested).
- packages/*
- auth, db, observability, services, types — five coarse shared packages.
05Engineering decisions
What was chosen, and what it cost
- D1
Socket server is transport-only
apps/socket authenticates, authorizes, and fans out events. It never writes a Message to MongoDB; the Next.js API is the only writer and the only outbox producer.
- Rejected
- Persisting in the socket handler (stateful, schema-coupled) and dual-write (two writers, guaranteed drift).
- Cost
- Persist-then-broadcast ordering is a client contract, not a server guarantee. Clients reconcile the live event against the persisted REST view.
- D2
Authorization over an internal HTTP bridge (ADR-003)
The socket server makes trust decisions by calling private web endpoints guarded by a shared internal secret. Identity is re-validated against Mongo on connect, never from claims alone.
- Rejected
- Giving the socket server Mongoose models (re-couples the schema) and trusting JWT claims (a banned user keeps operating until token expiry).
- Cost
- A network hop per authorized operation, a dependency on web uptime, one unscoped shared secret, and a 45s revocation lag.
- D4
Transactional outbox as the integration boundary (ADR-002)
The domain change and an OutboxEvent commit in one Mongo transaction; the worker claims events with an atomic findOneAndUpdate.
- Rejected
- Direct RPC from web to worker (a worker outage drops work) and a dedicated queue (more operational surface).
- Cost
- At-least-once delivery, and the retry scanner requires a replica-set Mongo.
- D5
Layered idempotency instead of chasing exactly-once
Accept at-least-once and enforce idempotency at the event, action, and tool layers. The email tool forwards its key as the provider's Idempotency-Key header so the provider dedupes too.
- Cost
- The original tool key included runId, so a lease steal could re-execute a side effect. The shipped key is run-independent: SHA-256(taskId | stepId | toolName | params).
- D6
Distributed lease and heartbeat for one writer per task
A DB-held lease on the Task document, acquired atomically and renewed every leaseMs/3. A failed heartbeat aborts the in-flight LLM call and tool fetch.
- Rejected
- Optimistic concurrency alone (does not reliably guard between fetch and save) and a Redis-only lock (lease state would live apart from task state).
- Cost
- Heartbeat tuning, steal windows, and replica-set assumptions for transactional paths.
- D7
Execution state machine is authoritative (ADR-001)
Fine-grained executionState is the enforcement boundary. Legacy lifecycle fields are projected from it at write time; divergence is logged, not auto-repaired.
- Cost
- Two fields still exist on the document.
06Tradeoffs
Known limits, written down
Persist-then-broadcast is a client contract: a misbehaving client can emit without persisting, and receivers see a ghost message until they refresh.
Authorization costs an HTTP hop per privileged socket operation and depends on the web app being up.
Transactional paths and the outbox retry scanner need a replica-set MongoDB.
Delivery is at-least-once; correctness rests on idempotency at the event, action and tool layers.
The dev-mode in-memory Redis shim is not horizontally safe.
These are open-source projects, not services operated with production user traffic. Scaling notes describe the intended shape, not measured load.
07Technology
What it runs on
- Runtime
- Node.js 24
- TypeScript
- pnpm
- Turborepo
- Web
- Next.js 15 (App Router)
- Real-time
- Socket.IO
- Redis adapter
- Data
- MongoDB
- Redis
- AI
- OpenAI
- OpenAI-compatible
- Hugging Face
- Delivery
- Docker Compose
- nginx
- Playwright e2e
- GitHub Actions
08How it grew
One repository, three stages
Stage 1
ChatApp
Real-time messaging foundation
Added: The real-time layer: HTTP persists first, Socket.IO only transports, Redis carries the adapter and presence.
Stage 2
Agent Mesh AI
Worker / orchestration
Added: A task worker, a transactional outbox, distributed leases and an LLM provider abstraction.
Stage 3
Semantask
Current platform
Added: The suggest-first contract: conversation becomes suggestions, people approve, policy gates tools.
ChatApp and Agent Mesh AI are earlier names for stages of Semantask, not separate products. One repository: github.com/harshlocham/semantask(opens in a new tab)
09Proof
Read the code
Recent public activity
- Organization coordination dashboard
- Playwright end-to-end proof harness
- Onboarding wizard and first conversation for new accounts
- Runtime migrated to Node 24 LTS
- Design system foundation
From the repository's public history.